BrainPad Advent Calendar 2025 3日目の記事になります
--
株式会社ブレインパッド プロダクトユニットの津久井です。
弊社は「データ活用の促進を通じて持続可能な未来をつくる」をミッションに、
データ分析支援やSaaSプロダクトの提供を通じて、企業の「データ活用の日常化」を
推進しております。
現在私は、AI SaaSのエージェントリンリーの開発リードを担当しています。
( 最近Qiitaバズ師の社内メンバーが、イイ感じのヘッダ文を作ってくれました。kansha! )
はじめに
cc-sddなど、AIコーディングを用いた仕様駆動開発(...)において
元となるmarkdownをリポジトリ管理することは徐々にトライされていますが、
リポジトリ外でもドキュメントとしてmarkdownの内容をステークホルダ
(特に非エンジニア)が閲覧可能な状態にすることが望ましいケースも多くあります。
今回は、その一例として、markdownの仕様書をディレクトリの階層ごと
Atlassian Confluenceに同期させるツール「Mark」を、使い方とともに紹介します。
(サンプルリポジトリのURLも最後に記載しています)
※以後の成果物は、全てサンプルコード由来です。
Markとは
Mark は Go 言語で書かれた CLIツールで、Markdown ファイルを
Atlassian Confluence(以下confl) ページに自動同期することを目的としています。
Markdownファイル内に、下記のようなHTMLコメント形式で
<!-- Space: スペースのKEY -->
<!-- Parent: 親のconflページ名 -->
<!-- Title: Overview -->
プロジェクト概要…
メタデータを記述すると、指定したスペースの配下に必要なページを作成し、
内容をアップロードしてくれます。(要 confl API Key)
作成されたconflページ
Mermaidも、ちゃんとPNG画像に変換してconflページに埋め込んでくれます。
インストール
Mark は GitHub のリリースからバイナリをダウンロードするか、Homebrew でインストールできます。例として Homebrew を使う場合は以下のとおりです。
brew tap kovetskiy/mark
brew install mark
Go が導入済みなら
go install github.com/kovetskiy/mark@latest
でも取得できます。
メタデータの基本的な記述方法
Markは、Markdown ファイル内のメタデータを基にページ階層を決定します。
たとえば以下のようなディレクトリ構造を 依存関係をそのままに反映したい場合、
.
├── project
│ ├── overview.md
│ └── api
│ ├── index.md
│ └── auth.md
└── intro.md
各markdownファイルのメタデータは以下のようになります。
<!-- Title: intro -->
# はじめに
Markdown 本文…
<!-- Parent: intro -->
<!-- Parent: project -->
<!-- Title: overview -->
# 概要
プロジェクト概要…
<!-- Parent: project -->
<!-- Parent: api -->
<!-- Title: index -->
# API一覧
...
<!-- Parent: project -->
<!-- Parent: api -->
<!-- Title: auth -->
# 認証API
...
Parent ヘッダーを複数並べることで、conflページの階層が作られます。
例: project/api/auth.md ... project → api → auth
(親ページが存在しない場合は Mark が自動生成します)
実行
共通設定
~/.config/mark.toml に共通設定を記載することで、コマンド実行時に利用されます。
username = "your-email@example.com" # ユーザー名
password = "your-api-token" # Confluence API Key
base-url = "https://<組織名>.atlassian.net/wiki" # ベースのURL
space = "~********" # conflのスペースID
title-from-h1 = true # markdownのh1要素を、ページのタイトルに設定する
(オプション詳細は、公式を参照下さい)
markコマンド実行
mark -f (markdownファイル)
# mark.tomlを明示的にする場合(私の環境は、明示的に指定しないと反映されませんでした)
mark -f (各markdownファイル) -c ~/.config/mark.toml
conflのスペース"docs"配下に、指定した階層でconflページが作られます。
markdownに手を加えずディレクトリごとconflに同期させる方法
前述のベーシックな手段の場合はmarkdownごとにメタデータを管理する必要があり、
やや手間がかかりますが、こちらのページでは markdownに手を加えず、コマンドラインと
オプションを工夫して、markdownに手を加えずディレクトリごとconflに同期させる方法が
紹介されています。(当ページの本題)
実現方法
markのコマンドラインには --parents というオプションがあり、
--parents project/api
のようにルートページからのPathを与えることで、前述の
# 親ページ指定
<!-- Parent: project --> <!-- Parent: api -->
のようなメタデータと同様の親ページ指定を、ファイル単位ですることが可能です。
このオプションをfindコマンドなどと組み合わせることで、
コマンドオプションでファイルごとに階層を指定することが可能になります。
(=メタデータの記述が不要になり、markdownそのものを使い回せる)
# docsディレクトリ配下のmarkdownのPathを取得し、階層を指定してmarkを実行
for file in `find docs -name "*.md"`; do
mark -f "$file" -c ~/.config/mark.toml --parents $(dirname "$file")
# 環境に合わせて、--parentsのpathの先頭に スペースのルートページ名が必要になる。
# ( /wiki/spaces/(スペース名)/overview でアクセスできるページの名前 )
# ※下記はルートページ名が「概要」の場合の例
# mark -f "$file" -c ~/.config/mark.toml --parents 概要/$(dirname "$file")
done
※オプションは、前述のmark.tomlで設定した内容
同期するmarkdown
docs
├── intro.md
└── project
├── api
│ ├── auth.md
│ └── index.md
└── overview.md
前述のmarkdownからメタデータを除いたものを同期します。
(メタデータ管理を避ける)
# はじめに
Markdown 本文…
# 概要
プロジェクト概要…
# API一覧
...
# 認証API
...
上記を docs というページ配下に作成します。
スクリプト実行ログ
Syncing docs/intro.md
2025-11-27 16:37:13.018 INFO processing docs/intro.md
2025-11-27 16:37:13.725 WARN page "docs" is not found
2025-11-27 16:37:14.864 INFO page will be stored under path: 概要 > docs > Intro
2025-11-27 16:37:18.048 INFO page successfully updated: https://xxxxx.atlassian.net/wiki/spaces/~xxxxx/pages/xxxxx/Intro
https://xxxxx.atlassian.net/wiki/spaces/~xxxxx/pages/xxxxx/Intro
Syncing docs/project/overview.md
2025-11-27 16:37:18.780 INFO processing docs/project/overview.md
2025-11-27 16:37:19.678 WARN page "project" is not found
2025-11-27 16:37:21.237 INFO page will be stored under path: 概要 > docs > project > Overview
2025-11-27 16:37:24.858 INFO page successfully updated: https://xxxxx.atlassian.net/wiki/spaces/~xxxxx/pages/xxxxx/Overview
https://xxxxx.atlassian.net/wiki/spaces/~xxxxx/pages/xxxxx/Overview
Syncing docs/project/api/index.md
2025-11-27 16:37:25.561 INFO processing docs/project/api/index.md
2025-11-27 16:37:26.293 WARN page "api" is not found
2025-11-27 16:37:28.076 INFO page will be stored under path: 概要 > docs > project > api > Index
2025-11-27 16:37:31.295 INFO page successfully updated: https://xxxxx.atlassian.net/wiki/spaces/~xxxxx/pages/xxxxx/Index
https://xxxxx.atlassian.net/wiki/spaces/~xxxxx/pages/xxxxx/Index
Syncing docs/project/api/auth.md
2025-11-27 16:37:32.167 INFO processing docs/project/api/auth.md
2025-11-27 16:37:33.691 INFO page will be stored under path: 概要 > docs > project > api > Auth
2025-11-27 16:37:37.021 INFO page successfully updated: https://xxxxxatlassian.net/wiki/spaces/~xxxxx/pages/xxxxx/Auth
https://xxxxx.atlassian.net/wiki/spaces/~xxxxx/pages/xxxxx/Auth
実行結果
markdownに手を加えることなく、階層を保って同期されました。
注意
1.h1要素はタイトル専用にする
markdown内の # の要素は メタデータの Title: を入れないと
タイトル扱いとなってconflの本文から消えるため、
メタデータを追加しない場合は タイトル以外の内容は ## から始めると良さそうです。
2.conflページ名は一意にする
スペース内に、作成するconflと同名のページがある場合、作成に失敗します。
3.Title変更は、別のページ扱いになる
メタデータの Title: を変更すると、別のページとしてconflに反映されます。
おわりに
紹介したメタデータ付き/メタデータ無しのmarkdownサンプルとスクリプトは、下記に用意しました。
同期ツール自体はほかにもいくつかあるのですが、
markdownに手を加えることなくシンプルな実現方法で
conflへの同期が可能なMarkをまず紹介しました。
明日 12/4は、プロダクトユニットのホープ、データエンジニアの@aaaabbbbbさんです!




