Obsidian と MkDocs と GitHubActions と Netlify で君だけの最強日記環境を作りあげろ!
この記事は GMOペパボ 2022 Advent Calendar の23日目 です。昨日は僕と同じ Android 開発メンバーの kyu さんでした。
この記事を読む前に
すべて終わってから見つけてしまったんですが、タイトルを実現するだけであれば obsidian-publish-mkdocs というプラグインを使えばできそうな気がします
特定ファイルの公開など自分の実現したいことはそのプラグインでは達成できなかったので地道に用意していきます。
はじめに
去年はリモートワークなら猫と暮らせという記事を書きました。今年はその猫をみんなに自慢する方法のお話です?
ペパボでは 行動指針 の1つに みんなと仲良くすること という項目があります。行動指針それぞれについては こちらのテックブログがハイコンテキストで紹介している のでご覧ください。
誰かと仲良くするためにはお互いを理解する必要があります。そして重要なのは、まず自分を理解してもらうことが大切です。
なのでペパボでは行動指針にもあるようにアウトプットすることを推奨しており、アウトプット文化の1つとして 日報・日記 が存在します。
1行や3行でもその日のアクションや思ったことなどをチームを超えたさまざまな人が見れる場所にアウトプットすることで、自分の影響を広げ理解してもらうことができます。それが毎日5~10分で思ったその日から実行できる みんなと仲良くする ためのアウトプット 日報・日記 です。これを SNK (Souda Ni(ppo|kki) wo Kakou) と呼んでいるとかなんとか。またペパボでは Notion に日報置き場があり、そこに投稿された日報をさまざまな人に気づいてもらえるように Slack に通知するようにしています。
さてそんな素敵な日報・日記文化ですが、当然自分もやってみようと思いました。せっかくなので社内で閉じずもっとたくさんの人が見えるようにと。
ですが、自分はとてもめんどくさがりなのでこういう Web 上に公開する日記やブログはすぐに続かなくなってしまうのです。なぜ続かないかと言うと、
- 常駐しないサービスのページを開き
- 「+」のような新しい記事を作成するボタンをクリックし
- 専用のテキストフォームに入力し
- 「投稿する」ボタンをクリックする
- そしてそれぞれにリクエストが飛び画面が切り替わるため大なり小なり待機時間が存在する
- さらに投稿した内容は事前にテキストエディタで書いてあるのでテキストフォームにコピペする必要があり二度手間
が非常に煩わしく感じるためです。日記は継続することに意味があると思いますが、これを毎日やると思うととてつもない無力感にさいなまれます。そして自分はデザインを考えるのが圧倒的に下手なので wordpress なりで始めるまでのハードルが激高です。
でもせっかくなので1日1行とかでもいいから続けていく方法はないかと考えたところ、日常的に常駐させてメモをとったり何か書いたりしている Obsidian の便利機能 Daily Notes に目をつけました。シュッと書いて勝手に公開されてるとストレスフリーで継続できそうだな〜と思いました。そしてマークダウンで書いたものがそれなりに見れるフォーマットになればさらにハッピーだと思い MkDocs に目をつけました。これらをいい感じにすればいい感じにストレスフリーな日記継続環境ができるのでは、と
この記事はとんでもなくめんどくさがりな自分がみんなと仲良くなりたくてなんとか日記環境を整えた話です!
利用するもの
Obsidian
今までマークダウンエディタは BoostNote を使っていたのですが方針転換があったのか自分に合わなくなってきてそろそろ変えたいな〜と思っていたころ、ちょうど Notion がワイワイしてきた辺りに Obsidian いいよとレビューを見かけ使ってみたらこれだ!となって以来ローカルテキストエディタとして愛用しています。
- ユーザ登録がない
- マークダウンのプレビューが確認しやすい
- marmeid.js のプレビューができる
- 公式のモバイルアプリがあって同期できる
を前提としてモダンな UI で利用にストレスが少なくできれば無料のエディタを探していたため自分のユースケースにとてもマッチしています。
そして Obsidian はさまざまなプラグインが利用でき、この記事でフォーカスするのが Daily Notes と Obsidian Git です。
Daily Notes はショートカット1つでその日のファイルを作成し開く(すでに存在する場合は該当のファイルを開く)ことができます。テンプレートも用意できるためシュッとファイルを用意して書き始めることができます。Daily Notes という名前もそのまま日記として使っていけるためこれは!となりました。
そして Obsidian のファイル同期方法として Obsidian Git があります。その名前の通り GitHub のリポジトリを Obsidian のバックアップ先として利用するものです。Obsidian Git が定期的に commit/push をしてくれます。
MkDocs
md ファイルを元にいい感じに HTML を生成してくれる Python 製のツールです。さまざまなプラグインが用意されていて、個人的にはマテリアルデザインな UI にできるプラグインが用意されているのが気に入っています。
少し趣旨はずれますが、今回の内容は日記に限らず GitHub を利用している開発チームの中でドキュメントを運用する場合にも非常に有用です。
GitHub Actions
Obsidian Git が出てきたときに察したかもしれませんが、日記サイトのデプロイを自動化するために Github Actions を利用します。また、MkDocs には GitHub Actions を利用して GitHub Pages にデプロイすることを考えて作られたコマンドも存在し相性バツグンです。それと Twitter に通知するためにも利用します。
Netlify
GitHub と連携が可能な静的コンテンツのホスティングサービスの1つです。
GitHub Actions を利用するのであれば GitHub Pages でホストするのが一番都合がいいとは思います。しかし、今回 private リポジトリのファイルをホストする必要があるため GitHub Pages だと課金する必要があり、なるべく無料ですぐに始められることを前提にしていたので Netlify を利用します。カスタムドメインの運用が無料でできるのもとても嬉しいです。
環境構築
Obsidian のインストールとプラグインの有効化
先に GitHub に Obsidian 用のリポジトリを作成しておくとスムーズです。特にこだわりがなければ README を追加してクローンしておきましょう。
- まずは Obsidian からダウンロードしてインストール
- Obsidian を開いて
Open folder as vault
でクローンしたディレクトリを選択 - Obsidian の
Settings > Community plugins
を開いてRestricted mode
を OFF - View が切り替わるので
Community Plugins > Blowse
から Obsidian Git を探してインストール Installed Plugins
に Obsidian Git が追加されているのでスイッチを ON
- ついでに
Core plugins > Daily Notes
のスイッチもデフォルトで OFF なので ON にする
自動で同期させるには Obsidian Git の設定から Vault backup interval (minutes)
を設定しておきます。適当にファイルを作成して指定した時間後に GitHub に push されていれば成功です。また、手動で更新したい場合のために Hotkeys
から commit や push のショートカットを設定しておくといいかもしれません。
Daily Notes の設定
Daily Notes はファイル名、出力先、テンプレートを設定できます。Settings > Core plugins > Daily Notes
から設定しておくといいと思います。
自分はこんな感じに設定しています。テンプレートは MkDocs でメタデータを設定できるのでそれを指定しています。
---
title: {{date:YYYY/M/DD}}
descirption: {{date:YYYY/M/DD}} tick-taku's diary
date: {{date:YYYY-MM-DD}}
author: tick-taku
og_image: og_image.png
---
## Topics
-
MkDocs で HTML の生成
次に MkDocs で作成した md ファイルを元に静的コンテンツを生成します。最終的に GitHub Actions で動かすのでローカルにインストールする必要はありませんが手元で確認したいこともあるので 一般的な使い方 を載せておきます。必要なければ飛ばして構いません。
- Python がインストールされていることを確認
python --version
Python 3.10.8
- MkDocs をインストール(今回はマテリアルデザインを前提とします)
pip install mkdocs-material
mkdocs --version
mkdocs, version 1.4.2 from /opt/homebrew/lib/python3.10/site-packages/mkdocs (Python 3.10)
無事インストールされれば MkDocs でビルドするための準備をします。
プロジェクトルートで以下を実行するとビルド用の設定ファイルとビルド対象となる md ファイル格納ディレクトリが用意されます。docs 内に適当にファイルを作成しましょう。index.md は必須です。
mkdocs new .
.
├── README.md
├── mkdocs.yml
└── docs
└── index.md
最後にビルドしてサーバを起動させるとローカルで確認できます。ブラウザでアクセスして表示されていれば成功です。
mkdocs build
mkdocs serve
INFO - [16:40:39] Serving on http://127.0.0.1:8000/
最後に MkDocs は yml ファイルに色々設定できるためこれはと思ったものを紹介しておきます。その他にもさまざまな設定ができます。こちら が非常にまとまっていて参考になりました。
ダークテーマ
マテリアルデザインテーマを利用している場合 scheme: slate
でダークテーマにできます。僕は白背景だと目が痛くなるのでこれは本当に助かりました。
theme:
name: material
palette:
scheme: slate
primary: blue
accent: orange
mermaid のプレビュー
マークダウンエディタを探すときの条件に入れるくらい作図は memaid で書きます。MkDocs も設定次第でプレビューしてくれるので非常に助かっています。
markdown_extensions:
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
favicon
サイトのヘッダに表示する logo や favicon を指定できます。画像ファイルは docs 直下に配置する必要があります。
theme:
logo: logo.png
favicon: favicon.png
SNS アイコン
Font Awesome を利用して Twitter などの SNS アイコンとリンクをフッターに表示できます。
extra:
social:
- icon: fontawesome/brands/twitter
link: 'https://twitter.com/'
OGP の設定
公式のプラグインとかではないですが、出力する HTML をオーバーライドすることができるため独自に OGP のメタデータを設定できます。詳しくはこちらをご参考ください。
GitHub Actions でデプロイと Netlify で公開
GitHub Actions のワークフローを用意
続いて編集した md ファイルをデプロイして公開していきます。
MkDocs には GitHub Actions + GitHub Pages を想定した gh-deploy
コマンドが用意されています。Obsidian Git の定期バックアップで更新されていくタイミングで走るように以下のようなワークフローを用意します。
name: Deploy
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.10.8'
- name: Install requirements
run: |
python -m pip install --upgrade pip
pip install mkdocs-material
- name: Build MkDocs
run: mkdocs build --verbose --clean
- name: Deploy to GitHub Pages
run: mkdocs gh-deploy --force
成功すると gh-pages
というブランチが生成されそのブランチに mkdocs build
したファイルが展開されます。GitHub Pages であればこの gh-pages
ブランチを指定すれば公開も完了です。
ただし僕の場合は private リポジトリだったため GitHub Pages を利用するためには課金が必要だったので無料で使える Netlify を選択しました。
Netlify で公開
最後に Netlify で公開していきます。
- Netlify に Sign up
New site
でサイトの公開を設定Connect to Git provider
で GitHub を選択し認証Pick a repository
で Obsidian のリポジトリを選択Deploy settings > Branch to deploy
にgh-pages
を選択しDeploy site
以上でしばらくすると公開されます。site 情報の URL にアクセスして表示されていれば成功です。以降はワークフローでデプロイするたびに自動で更新されていきます。
カスタムドメインの設定
Netlify のデフォルトのドメインは複雑なのでカスタムドメインを設定しておくと誰かに URL 渡すときなどにシュッと渡せてオススメです。ペパボでは福利厚生でムームードメインで独自ドメインが取得できるため設定しました。
Domain settings > Add custom domain
で追加して VerifyCheck DNS configuration
をクリックしてネームサーバを確認- ムームードメインにログインし
ネームサーバ変更 > 取得したドメインで使用する
でそれらを登録
浸透すると設定したカスタムドメインでアクセスできるようになります。
更新通知を Twitter に流す
せっかく日記を書いても誰にも見てもらえないと悲しいですね。なのでいろんな人が見れる場所として Twitter に更新を通知させます。
とはいえ自動で同期させる性質上 「投稿した」がどの時点を指すのかは難しいと思います。なので僕は GitHub Actions で 昨日の投稿があれば Twitter に通知する
ようにスクリプトを用意しました。URL がファイル名となっておりかなり無理やり感があります。Twitter への投稿は API Key やアクセストークンが必要になるのでデベロッパー登録しておきましょう。
from datetime import datetime, timedelta
import os
import tweepy
DIARY_DIR = 'dailynote'
DIARY_WEB_URL = 'https://tick-taku.com'
def get_latest_diary_filename(date):
return f'{date.year}/{date.month}/{date:%Y_%-m_%-d}'
def is_diary_update(date):
latest_diary = get_latest_diary_filename(yesterday)
return os.path.exists(f'{DIARY_DIR}/{latest_diary}.md')
def post_tweet(date):
client = tweepy.Client(
consumer_key = os.environ.get('TWITTER_API_KEY'),
consumer_secret = os.environ.get('TWITTER_API_SECRET'),
access_token = os.environ.get('TWITTER_ACCESS_TOKEN'),
access_token_secret = os.environ.get('TWITTER_ACCESS_TOKEN_SECRET')
)
latest_diary = get_latest_diary_filename(yesterday)
message = f'{DIARY_WEB_URL}/{DIARY_DIR.lower()}/{latest_diary}'
print(client.create_tweet(text = message))
yesterday = datetime.now() - timedelta(1)
if is_diary_update(yesterday):
post_tweet(yesterday)
用意したスクリプトを毎日13時に実行するようにワークフローを作成します。なぜ13時かと言うとペパボのお昼休憩が13時でそれに合わせて社内 Slack にも様々な通知が飛ぶためです。
name: Post Update
on:
schedule:
- cron: '00 04 * * *'
jobs:
post:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.10.8'
- name: Install requirements
run: |
python -m pip install --upgrade pip
pip install tweepy
- name: Post update if exists
run: python post_update.py
env:
TWITTER_API_KEY: ${{ secrets.TWITTER_API_KEY }}
TWITTER_API_SECRET: ${{ secrets.TWITTER_API_SECRET }}
TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
以上で日記更新環境は整いました。
ディレクトリ構成が気に入らない!!
上で紹介している 一般的な使い方 であればおそらくこの記事は見なくてもそれ用のプラグインがありそうなのでそちらで済むかもしれません。
MkDocs は docs 配下のファイル全てを対象 にビルドします。一応 navigation が設定できるのですが設定するとファイルを追加する度に mkdocs.yml に手動で追加する必要があります。これが僕には非常に都合が悪かったのでした。
Obsidian には公開できないような書きなぐりや企業秘密などもまとめているので private リポジトリで運用しています。それらはビルド対象にしてほしくありません。また、docs という MkDocs のビルド用ディレクトリを常に意識する必要があり、mkdocs.yml が Obsidian で見えるのも煩わしいと感じました。あまり他のツールを Obsidian で意識したくありません。
なのでどうにかできないかと思ってビルド時に docs を用意するスクリプトを用意しました。
Obsidian のアプリから見えないように .mkdocs
というディレクトリを作り、その下に mkdocs.yml やスクリプトを置いていきます。MkDocs には docs 直下に index.md が必須なのでそれも用意する必要があります。最終的に下のような構造にしました。
.
├── .mkdocs
│ ├── conf.yml
│ ├── image
│ │ ├── icon.png
│ │ └── og_image.png
│ ├── mkdocs.yml
│ ├── override
│ │ └── main.html
│ └── scripts
│ ├── post_update.py
│ └── prepare_docs.py
├── README.md
├── dailynote
└── mkdocs
└── index.md
対象ファイルを追加したりするときにスクリプトをなるべく触らなくていいように conf.yml
を用意して対象ファイルを定義するようにしています。docs ディレクトリを作りそれらファイルの定義があればコピーします。
index: 'mkdocs/index.md'
logo: '.mkdocs/image/icon.png'
favicon: '.mkdocs/image/icon.png'
custom_theme: 'override'
docs:
- dailynote
- blog
- ノート
files:
- '.mkdocs/image/og_image.png'
import yaml
import shutil
import os
PREFERENCE_DIR = '.mkdocs'
DOCKS_DIR = 'docs'
os.makedirs(DOCKS_DIR, exist_ok=True)
with open(f'{PREFERENCE_DIR}/conf.yml') as conf_file:
conf = yaml.safe_load(conf_file)
shutil.copy(f'{PREFERENCE_DIR}/mkdocs.yml', './')
if 'index' in conf:
index = conf['index']
shutil.copy(index, f'{DOCKS_DIR}/{os.path.basename(index)}')
if 'logo' in conf:
logo = conf['logo']
shutil.copy(logo, f'{DOCKS_DIR}/{os.path.basename(logo)}')
if 'favicon' in conf:
favicon = conf['favicon']
shutil.copy(favicon, f'{DOCKS_DIR}/{os.path.basename(favicon)}')
if 'custom_theme' in conf:
theme = conf['custom_theme']
shutil.copytree(f'{PREFERENCE_DIR}/{theme}', theme, dirs_exist_ok=True)
for doc in conf['docs']:
shutil.copytree(doc, f'{DOCKS_DIR}/{doc}', dirs_exist_ok=True)
if 'files' in conf:
for file in conf['files']:
shutil.copy(file, f'{DOCKS_DIR}/{os.path.basename(file)}')
以上のスクリプトをデプロイ時のワークフローに組み込むことで公開したいファイルを制御しています。
name: Deploy
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.10.8'
- name: Install requirements
run: |
python -m pip install --upgrade pip
pip install mkdocs-material pyyaml
- name: Init MkDocs
run: python .mkdocs/scripts/prepare_docs.py
- name: Build MkDocs
run: mkdocs build --verbose --clean
- name: Deploy to GitHub Pages
run: mkdocs gh-deploy --force
これで Obsidian 上では MkDocs を意識することなくローカルとサイトの同期が実現できました。
番外編:Android でも Obsidian 使いたい
当然 PC 使わない日に日記更新するためにわざわざ立ち上げるのめんどくさいですよね。なので自分はマークダウンエディタを選ぶときの基準に公式のモバイルアプリと同期できることを意識しています。
Obsidian はモバイルアプリが用意されています。そして Obsidian Git も最近モバイルに対応したそうです。詳しくは Wiki にかかれていましたのでそちらをご覧ください。モバイルなので仕方ないんですが Git が隠蔽されていて確認できないのよくわかんなくて泣いちゃいそうです。
ちなみに僕はまだ試していません。Termux を導入して普通に Git を操作しています。導入についてはこのあたりを参考にするとよさそうです。
最後に
今回いろいろ試してみたリポジトリはこちらです。いろんなツールをキメラしているので何かあったときに泣きそうな気もしつつ今は快適に過ごしているのでよしとします。今後 Twitter の埋め込みに対応したり、更新通知やビルド準備など結構無理やり感があるのでもっとスマートにしていきたいと思っています。
一応日記を前提としていますがブログとかも同じですので、ブログサイトの運用が煩わしいめんどくさがりな方は一度設定すると後はかなりストレスが軽くなると思います。この記事もこの方法で公開までしています。
ちょっと工夫すれば、例えば GitHub Actions と Notion API を使って Obsidian で書いたものを Notion に投稿したり、GitHub Pages でドキュメントを公開してチームで差分管理しながら運用したりなどいろいろできそうです。Obsidian 最高!
継続して何かを続けるためには実行するまでのハードルは限りなく低い方が好ましいですね。みなさんも自分にあった方法で日報・日記を書いてたくさんの人と仲良くなりましょう!
明日はペパカレ入社でバリバリやっているつるPさんです。メリーメリークリスマス