🐍
設計書や仕様書のMarkdownファイルをまとめる方法
設計書や仕様書のMarkdownファイルをまとめる方法
## 導入
- 普段、設計書や仕様書をMarkdownで管理している中で、AIに食わせたり、レビューに使うために単一ファイル化したいことがよくあります。そこで簡単に使えるスクリプトを作りました。
目的
- 仕様書・設計書などで、Makrdownの記載は有効だと考えています。
- ただし、資料が多くなると、ファイル自体分散したくなる
- 生成AIなど利用しているときには仕様書・設計書などの提示が有効で、そのためには上記ファイル群を単一にまとめた方が使い勝手いい
- あくまでもGeminiの場合、ChatGPTなどは、Githubのパス指定や、Zipファイルなどで提示可能の模様
- その際の作業を迷わないようにしたいがための「案」と「ツール」(スクリプト)です。
前提
- 特定のディレクトリ「docs」等にファイルをまとめて保存
- 自分はプロジェクト以下のような形で保存しています。
DOCS
│ README.md # ディレクトリの意味合い
│ 01_overview.md # 概要
│ 02_architecture_design.md # 基板の設計
・・・
│ 05_api_spec.md # APIの設計
│
├─features # 特定機能の機能について保存場所
│ │ DisplayChart.md # DisplayChartの仕様書
・・・
│ │ ViewHistory.md # 参照履歴について仕様書
│ │
│ └─images
│ DisplayChart_image01.png # DisplayChart.mdで利用する画像
・・・
│
└─images
01_overview_image01.drawio.png # 01_overviewで利用する画像
- 上記構成で以下のスクリプト作製
- 指定されたディレクトリ以下のすべてのMarkdownファイルを結合し、単一のMarkdownファイルとして出力します。
- 結合順は、まずトップレベルのファイルをファイル名順に、次にサブディレクトリ内のファイルをパス名順に結合します。ただしREADME.MDは特殊扱いで必ず統合後ファイルの先頭になります。
- また、'images'という名前のサブディレクトリを見つけ、その中のファイルを出力先ディレクトリ内の'images'フォルダにコピーします。
- Args:
- output_file_path (str): 結合された内容を保存する新しいファイルのパス。
import os
import shutil
def combine_markdown_files(input_directory, output_file_path):
combined_content = []
# 出力ディレクトリのパスを取得し、出力用のimagesディレクトリを準備
output_dir = os.path.dirname(output_file_path)
output_images_dir = os.path.join(output_dir, 'images')
os.makedirs(output_images_dir, exist_ok=True)
# 入力ディレクトリが存在するか確認
if not os.path.isdir(input_directory):
print(f"エラー: 指定されたディレクトリ '{input_directory}' が見つかりません。")
return
print(f"'{input_directory}' 内のMarkdownファイルと画像ファイルを検索中...")
# --- 1. ファイルパスの収集 ---
markdown_files = []
image_dirs = []
for root, dirs, files in os.walk(input_directory):
# 'images' ディレクトリは特別扱い
if 'images' in dirs:
image_dirs.append(os.path.join(root, 'images'))
dirs.remove('images') # これ以上 'images' ディレクトリ内を探索しない
for file_name in files:
if file_name.endswith(('.md', '.markdown')):
markdown_files.append(os.path.join(root, file_name))
# --- 2. Markdownファイルのソート ---
# トップレベルのファイルとサブディレクトリのファイルを分ける
top_level_files_raw = [
f for f in markdown_files
if os.path.dirname(os.path.normpath(f)) == os.path.normpath(input_directory)
]
sub_dir_files = sorted([
f for f in markdown_files
if os.path.dirname(os.path.normpath(f)) != os.path.normpath(input_directory)
])
# _README.md を特別扱い
readme_file = None
other_top_level_files = []
for f in top_level_files_raw:
if os.path.basename(f).lower() == 'readme.md': # ファイル名が 'README.md' なら特別扱い
readme_file = f
else:
other_top_level_files.append(f)
# 残りのトップレベルファイルをファイル名順にソート
sorted_other_top_level_files = sorted(other_top_level_files)
# 結合順序を確定
sorted_markdown_files = []
if readme_file:
sorted_markdown_files.append(readme_file)
sorted_markdown_files.extend(sorted_other_top_level_files)
sorted_markdown_files.extend(sub_dir_files)
# 3. 画像のコピー
print(f"画像のコピー先: '{output_images_dir}'")
for image_dir in image_dirs:
print(f" - 画像ディレクトリ '{image_dir}' を処理中...")
for file_name in os.listdir(image_dir):
source_path = os.path.join(image_dir, file_name)
destination_path = os.path.join(output_images_dir, file_name)
try:
shutil.copy2(source_path, destination_path)
print(f" - コピー: '{file_name}'")
except shutil.SameFileError:
print(f" - スキップ (コピー元と先が同じ): '{file_name}'")
except Exception as e:
print(f" - エラー: '{file_name}' のコピー中に問題が発生しました: {e}")
# --- 4. Markdownファイルの結合 ---
for file_path in sorted_markdown_files:
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
file_name = os.path.basename(file_path)
combined_content.append(f"# {file_name}\n\n")
combined_content.append(content)
combined_content.append("\n\n---\n\n")
print(f" - MD追加: '{file_path}'")
except Exception as e:
print(f" - エラー: '{file_path}' の読み込み中に問題が発生しました: {e}")
# --- 5. ファイルへの書き込み ---
if combined_content:
try:
with open(output_file_path, 'w', encoding='utf-8') as f:
f.write("".join(combined_content))
print(f"\nすべてのMarkdownファイルが '{output_file_path}' に結合されました。")
except Exception as e:
print(f"エラー: 結合されたファイルの書き込み中に問題が発生しました: {e}")
else:
print("\nMarkdownファイルが見つからなかったため、ファイルの結合は行われませんでした。")
# --- 使用例 ---
if __name__ == "__main__":
# ここを実際のパスに置き換えてください
input_dir = "./docs" # 例: カレントディレクトリ下の 'spec_docs' フォルダ
output_file = "./docs_combine/readme_all.md" # 出力するファイル名
# 実行
combine_markdown_files(input_dir, output_file)
# 別の例: カレントディレクトリにあるMDファイルを全て結合する場合
# input_dir_current = "."
# output_file_current = "./all_in_one.md"
# combine_markdown_files(input_dir_current, output_file_current)
# python ./docs_combine/combine_docs.py
- スクリプトを「python ./docs_combine/combine_docs.py」で実行することで保存場所にまとまったMDが作成されます。
- 対象保存先のImagesフォルダ以下もコピーされるのでMD内での画像参照も有効
- 同じディレクトリ内だと、ファイル名順でソートされます。ただし、README.MDは先頭扱い
- これで、複数ファイルに分かれていた、マークダウンファイルが画像含めて、単一ファイル、単一ディレクトリにまとまります。参照している画像も到底のフォルダにまとまるる形になります。
Discussion