🐻

ただ15行のシェルスクリプトで個人ナレッジマネジメントツールを作った話

2022/05/03に公開約5,700字

はじめに

自分が読んだ本や記事などを読む時に書いたノートを体系的に管理したいですよね。
現在優秀なナレッジマネジメントツールはありふれています。企業向けだと

  • Confluence
  • DocBase
  • Qiita Team

などがあります。個人向けは

  • Notion
  • HackMD
  • Boost Note

のようなシンプルで使いやすいツールがあります。マインドマップツールをさらに含めると数え切れません。

筆者自身はミニマリストです。

  • コードのようにGithubで自分のノートを管理したい
  • Webからマインドマップ形式になっているノートを確認したい
  • サブスグではなく、無料で使いたい

なので、個人ナレッジマネジメントツールを自作したいという発想に至りました。
結果としては下記の15行シェルスクリプト、GitHub ActionsとMarkdownマインドマップ変換ツールmarkmapで作りました。

https://aibazhang.github.io/
項目をクリックすると、各ノートのマインドマップを確認できます。

ソースはこちらです。

https://github.com/aibazhang/tiny-mindmap

それでは、解説していたきます。

Markdownからマインドマップ

筆者は昔からノートをMarkdownで書いてGitHubに上げて管理する習慣があります。しかし、ノートが長過ぎると、あるいはネストが深すぎると若干読みづらくなるので、Markdownで書いたものそのままマインドマップに変換したいです。
ネットで検索するとマインドマップツールは大抵有料版か制限付き無料版がほとんどです。シンプルで使いやすいかつ無料のエンジニア向けマインドマップがあるといいなと思いながら、Markmapという神ツールを見つけました。なんとMarkdownをいい感じにhtmlのマインドマップを変換してくれます。

https://github.com/gera2ld/markmap

使い方は2つのコマンドを打つだけです。

npm install -g markmap-cli
markmap note.md

VS Codeのextensionをインストールしてプレビューできるのも嬉しいですね。

https://marketplace.visualstudio.com/items?itemName=gera2ld.markmap-vscode

デプロイ

いつでもどこからでも自分のノートを確認したいので、Markmapで変換されたhtmlをWebサーバにデプロイする必要があります。もちろんHerokuなどのクラウドプロバイダーにデプロイしても全然問題ないですが、GitHubだけで完結させるとシンプルだと思うので、github.ioにデプロイすることにしました。

github.ioの使い方は公式にご参照ください

https://pages.github.com/

パッと見てマインドマップのhtmlパスをindex.htmlにリストとかの形式ぶち込んで終わりですが、JavaScript単体だけではファイルシステムにアクセスできないので、どうしてもNode.jsを使う必要があります。
やはりシンプルは今回の原則なので、できる限りリポジトリの中にNode.js関連のファイルを残したくないと思います。つまり、Markmapの実行とindex.htmlの作成という手順はロカールで行いません。GitHub Actionsの力を借りると簡単に実現できます。

下準備

まずindex.html.sampleを用意します。JavaScriptでやっていることとしては取得されたマインドマップのパスで、リスト(<li></li>)を作成します。フロントエンドフレームワークを使うのは大げさなので、ここではDOMを直接操作することにしました。
また、HTML_FILE_LISTは後ほどシェルスクリプトで置き換えます。シェルスクリプトを使うとNode.jsを使わずにhtmlのパスを取得できます。
CSSの詳細は説明しませんが、リポジトリから確認できます。


<!DOCTYPE html>

<html>
  <head>
    <title>Mindmap Wiki</title>
    <meta charset="UTF-8" />
    <link rel="stylesheet" href="style.css" />
  </head>

  <body>
    <div style="width: 600px; margin: 0 auto">
      <h1>Mindmap Wiki</h1>
      <ol class="gradient-list" id="list"></ol>
      <script>
        let data = [HTML_FILE_LIST];
        let list = document.getElementById("list");

        data.forEach((item) => {
          let li = document.createElement("li");
          let link = document.createElement("a");

          link.setAttribute("href", `${item}`);
          link.setAttribute("target", "_blank");
          // ./dist/sub/hoge.md.html -> hoge
          let title = item.split("/").slice(-1)[0].split(".")[0];

          if (title.includes("_")) {
            title = title.charAt(0).toUpperCase() + title.slice(1);
            link.textContent = title.split("_").join(" ");
          } else {
            link.textContent = title;
          }

          li.appendChild(link);
          list.appendChild(li);
        });
      </script>
    </div>
  </body>
</html>

ソース

Markmapの実行

何回も言及した15行のシェルスクリプトについて説明します。
まずはhtmlファイルを保存用のディレクトリを作成してから、すべての.mdファイルのパスを探します。

#!/bin/bash
mkdir -p ./html/tech ./html/nontech 
files=`find ./ -type f -name "*.md" ! -name "README.md" ! -path "./node_modules/*"`

.mdファイルに対してmarkmapを実行します。権限などがややこしくなるので、markmapはグローバルインストールではなく、node_modules/.binにインストールすると便利でしょう。

for file in $files;
do
  output_file="${file/md/html}"
  echo "transforming $file to $output_file"
  node_modules/.bin/markmap $file -o $output_file.html --no-open
done

index.htmlの作成

生成されたhtmlファイルのパスを探して、index.html.sampleの中のHTML_FILE_LISTを置き換えます。パスにダブルコーテーションをつける必要があるので、sedで置き換えるとうまくいきませんでした。今回はsedを諦めてawkを使っています。

find ./html -type f -name "*.html" ! -name "index.html" | \
  awk '{print "\""$0"\","}' | \
  xargs -0 -I{} awk -v list={} '{sub(/HTML_FILE_LIST/, list); print}' \
  index.html.sample > index.html

https://unix.stackexchange.com/questions/75310/why-is-sed-giving-me-an-error-about-an-unterminated-s

ワークフロー

上記スクリプトはローカルではなく、GitHub Actionsで実行すると環境構築の手間を省けます。
それでは実装を見ていきましょう。特に難しいことをやっていません。

  1. markmap-cliをインストールする
  2. 先ほど作成したシェルスクリプトmd2mindmap.shを実行する
  3. 生成されたhtmlファイルをサードパーティアクションgithub-push-actionを使ってリポジトリにpushする
name: Build Minadmap

on:
  push:
    branches:
      - "master"
    paths:
      - "md/**"
      - "./.github/workflows/build.yml"

jobs:
  build:
    runs-on: ubuntu-latest
    container: node:16

    steps:
      - uses: actions/checkout@v2

      - name: Install dependency
        run: npm install markmap-cli

      - name: Run convert script
        run: bash md2mindmap.sh

      - name: Commit files
        run: |
          git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git config --local user.name "github-actions[bot]"
          git add .
          git commit -m "Add changes" -a
      - name: Push changes
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: ${{ github.ref }}

ローカルで.mdを作成・編集・削除してmasterにpushすると、GitHub Actionsのパイプラインが発火されて、マインドマップが自動的にusername.github.ioにデプロイされます。

以上です。

最後に

いかがでしょうか?

先人の知恵を借りながら、ただ15行のシェルスクリプトでSimple&Tiny&Freeな個人ナレッジマネジメントツールを作りました。Git&GitHubの最も基礎的な知識があれば、コスト0で自分のナレッジマネジメントツールを構築可能です。

ぜひcloneして遊んでみてください

https://github.com/aibazhang/tiny-mindmap

2022.5.5 編集

Discussion

ログインするとコメントできます