JupyterNotebookでMarkdownの記事を書く(1) - 環境構築編
まずは動くところまで最低限の環境を構築していきます。
なにがしたいのか
そもそもも始まりは、
もともと、USDにしても他のコードにしても
Pythonを利用してコードの動作などをテストする場合、JupyterNotebookを使用していて
そのNotebookをGithubPagesで公開しています。
Pythonを使用する場合、
こういったコードの挙動であったり、書き方、Tips的なものは
Notebookで書くと、
実行結果を表示できたり、ipynbをダウンロードすればそのまま再現できるというメリットがあったり、
さらに、NotebookにはMarkdownを書くこともできるので
コードの間に補足を入れたりみたいなこともできるので
コード解説を主体にしつつ、Markdownで補足を書くような記事は
Notebookで書きたいなーと常々思っていました。
mkdocsのページは、GithubにPushする前に事前に ipynb -> md のコンバートをしていたのですが
Zennに上げる場合は事前にコンバートもめんどくさい。
というわけで、表題の通りなのですが
今回はJupyterNotebook(ipynb)をPushしたらZennの記事として公開できるようにしてみよう!
と思います。
Githubのリポジトリを準備する
まずはリポジトリを作ります。
最近のZennの更新で、複数のリポジトリから記事を構築できるようになったので
ipynbから記事を作るほうはリポジトリを分けておきます。
リポジトリ以下の構造は、Zennの記事 md を配置する articles と、
その記事のもとになるipynb置き場、そして ipynb to markdown 用のテンプレートファイルの3つ。
+GithubActionsの処理を書く yml 置き場(.github/workflows ) です。
Dockerで環境を作る
リポジトリを作ったら、次にGithubActionsを実行するための環境を用意します。
環境は、mkdocs のときのようにpipenvを利用してもよいのですが
今回は勉強をかねてDockerを使います。
事前にDockerfileを用意して、DockerHubにイメージをアップします。
Dockerfileはこちらのサイト様を参考に作成して、
RUN pip install nbconvert
markdownにコンバートするためのnbconvertのインストールと
RUN apt-get update && apt-get install -y git
を追加します。
GithubActionsでは、GitにPushする必要があるので git を使えるようにする必要がありますが
Dockerのデフォルトの環境だとインストールされていません。
なので、 Dockerfile側に gitインストールを入れておかないと、のちの処理でエラー になります。
templateを書く
Dockerの準備ができたら、 markdown に変換するあたりを用意します。
細かいところは、以前 mkdocs 用に書いたこちらの記事と同じですが、Zenn用+Docker上で動かすために
少し修正をします。
# -*- coding: utf-8 -*-
import glob
import subprocess
import os.path
import os
os.makedirs('articles', exist_ok=True)
for ipynb in glob.glob("./ipynb/*.ipynb"):
p = subprocess.Popen(['python',
'-m', 'nbconvert',
'--to', 'markdown',
'--output', f'../articles/{os.path.splitext(os.path.basename(ipynb))[0]}.md',
'--template', 'template/jupyter_template.tpl',
ipynb])
p.wait()
GithubActionsで呼び出すipynb to markdown を実行するコマンドを リポジトリ直下に作成します。
これで ipynb 以下にあるファイルを markdown に変換することができます。
{% extends 'markdown.tpl'%}
{% block header %}
{% endblock header %}
{% block markdowncell%}
{{cell.source}}
{% endblock markdowncell%}
{% block in_prompt %}
{% if cell.execution_count > 0%}
#### [{{ cell.execution_count if cell.execution_count else ' ' }}]:
{% endif %}
{% endblock in_prompt %}
{% block stream %}
:::message{{"\n"}}
{{- output.text|replace("^ ","") -}}
:::
{% endblock stream %}
{% block execute_result%}
:::message
{{- output.text|replace("^ ","") -}}
:::
{% endblock execute_result%}
{% block error %}
:::message alert
{{- output.text|replace("^ ","") -}}
:::
{% endblock error %}
markdown に変換するためのテンプレートはこんな感じになります。
mkdocsのときは、メッセージの表記部分はインデントする必要があったので問題なかったのですが
Zennの場合インデントをしてしまうと
ちょっとイケてない見た目になるので、replaceを使うことによって
行頭のスペース4つ分を削ります。
このテンプレートはJinjaで作られているので、Jinjaのテンプレートをもう少し理解すれば
いい感じの見た目に改良でる(はず)
結果。
GithubActionsを書く
準備ができたので、最後にGithubActionsで ↑のPythonスクリプトを実行して
markdownをarticles 下にPushするようにします。
name: IpynbConvert
on:
push:
branches: [master]
jobs:
build:
runs-on: ubuntu-latest
container: docker://fereria/nbconvert:latest
steps:
- name: Git config
run: |
git config --global core.symlinks true
git config --global user.name "Megumi Ando"
git config --global user.email "###@###"
- uses: actions/checkout@master
- name: Convert Ipynb to Markdown
run: python ipynb_convert.py
- name: push
run: |
git add articles/*.md
git commit -m "add md" -a
git push
masterブランチになにかをPushしたら指定したDockerのコンテナを利用して
ipynb_convert.py を実行して、その結果をpushします。
jobs.build.steps以下に実際に実行したいプロセスを書いていきます。
自分で指定のコマンドを実行する場合は name と run( run: | 以降複数行書ける)
uses を使用すると、指定のリポジトリ以下にある自動処理を使うことができます。
uses: actions/checkout@master の場合は、
actions以下のcheckoutリポジトリ(https://github.com/actions/checkout)のmasterブランチの
内容を実行しろ...という意味になります。
その名の通りこのGithubActionsを実行しているリポジトリをチェックアウトするための
Actionです。
チェックアウトしたら、 python コマンドを実行します。
これで ipynbが markdown に変換されるので、その後Pushすれば完了です。
結果、
これが こうなります。記事は非公開にしてるのでZenn上には表示していませんが
プレビューするとこんな感じになります。
サンプルリポジトリ。
次回の改良
一応やりたいことはできたのですが、このままだといくつか問題があります。
- ipynb以外がPushされても実行されてしまう
- 変更がない場合GithubActionsがエラー扱いになってしまう
- Zenn用のタイトル指定周りがJupyterNotebook上だとみため崩壊してるのがやだ
- 記事の最後に ipynb へのリンクがほしい
- JinjaTemplateの改良(見た目の改善)
と、不満なところが結構あるので
もう少しGithubActionsのドキュメントを読みつつ処理を改良して
実際に公開できるところまでやろうと思います。
Discussion