↑で書いていたtoolをgolangに書き直しました。ついでに最新のGrowiのスキーマにも対応しました。
mercurialからGitにリポジトリを変換したので、試したい方はこちらをご利用ください。
使い方
$./growibackup ${revison.json} ${backup_dir}
エントリの内容はrevison.jsonに書かれているので、それを指定します。 docker-composeで動かしている場合は、こんな感じのシェルスクリプト化すると楽です。
DAY=`date "+%Y-%m-%d"` REVJSON=revision_back_${DAY}.json docker exec growi_mongo_1 mongoexport -d growi -c revisions --pretty --jsonArray --out ${REVJSON} docker cp growi_mongo_1:${REVJSON} . docker exec growi_mongo_1 rm ${REVJSON} ./growibackup ${REVJSON} Growi
生成されたmarkdownはPerlの時と同様にシンプルなファイルになっています。 研究室ではmercurialを使っているので、hgリポジトリ化してみました。
DB関係
revisionsのスキーマが変わったのか、mongodbのバージョンアップをしたためかわかりませんが、以前実装した時とrevisionsの要素の型が異なっていました。
以前はこんな感じでしたが
type Revision struct { ID string `json:"_id"` Format string `json:"format"` CreatedAt time.Time `json:"createdAt"` Path string `json:"path"` Body string `json:"body"` Author string `json:"author"` HasDiffToPrev bool `json:"hasDiffToPrev"` V int `json:"__v"` }
今はこんな感じです
type Revision struct { ID struct { Oid string `json:"$oid"` } `json:"_id"` Format string `json:"format"` CreatedAt struct { Date time.Time `json:"$date"` } `json:"createdAt"` Path string `json:"path"` Body string `json:"body"` Author struct { Oid string `json:"$oid"` } `json:"author"` HasDiffToPrev bool `json:"hasDiffToPrev"` V int `json:"__v"` }
jsonからgolangの構造体を作るにはJSON-to-Goを使うと便利です。
また以前はdocker exec growi_mongo_1 mongoexport -d growi -c revisions --out ${REVJSON}
みたいな感じでjson化していましたが、今日やってみたところrevisonの配列ではなくて、revisionが1件1件乗っている、invalidなjsonが返ってきました。
{ "_id": { "$oid": "5df9ce81f7f7970046c44609" }, "format": "markdown", "createdAt": { "$date": "2019-12-18T07:00:17.357Z" }, "path": "/user/anatofuz/note/2019/12/18", "body": "# 日報\n\n- nkmr先生の講義で発表\n- 実験2のTAをした\n- GearsOSの書き換え作業\n\n## GearsOSの書き換え\n\n- なんか`sys_read_impl.h`時代の名残があったので幾つかファイル を削除した\n - interface_impl headerとimplの2種類\n- そろそろsyscall interfaceを書くべき?\n - interfaceの仕様が結構混乱を招きそうな気配を感じている\n - チュートリアル>的な資料の充実...?", "author": { "$oid": "5df5ef37d744a60045dd1524" }, "__v": 0 } { "_id": { "$oid": "5df9e9baf7f7970046c4460b" }, "format": "markdown", "createdAt": { "$date": "2019-12-18T08:56:26.446Z" }, "path": "/user/anatofuz/note/2019/12/18", "body": "# 日報\n\n- nkmr先生の講義で発表\n- 実験2のTAをした\n- GearsOSの書き換え作業\n\n## GearsOSの書き換え\n\n- なんか`sys_read_impl.h`時代の名残があったので幾つかファイル を削除した\n - interface_impl headerとimplの2種類\n- そろそろsyscall interfaceを書くべき?\n - interfaceの仕様が結構混乱を招きそうな気配を感じている\n - チュートリアル>的な資料の充実...?\n \n## 次の継続に行く書き方\n\n`__code next(int ret_val,...)`\n- この `ret_val`は実装している方のitnerfaceに記述されている必要がある", "author": { "$oid": "5df5ef37d744a60045dd1524" }, "hasDiffToPrev": true, "__v": 0 }
色々試したところ、--jsonArray
をつければvalidなjsonとして返ってくるらしく、つけたところちゃんとrevisionの配列のjsonとして返ってきました。
Perlからの移植
自分で使う分にはPerlでいいんですが、後輩の引き継ぎとビルドの手軽さを考えるとgolangでの書き直しをしてみました。バイナリのポン置きしたいし。
もともとメインルーチンはPerlで書いていたので素朴に移植するみたいな感じでした。 ポインタ周りをあまり使って無くて、じゃぶじゃぶインスタンスを作りまくる感じにしたのでメモリには優しくなさそう....。
戸惑ったのはtime.Time
型の比較をするBefore
とAfter
というメソッドがあるのですが、当初「prev
よりrev
が新しかったら更新をする」という意図でこう書いていました。
これはrevisions.jsonにはエントリのすべての更新記録があるのですが、CVSで管理するので最新の1件だけあればいいので、それを特定する必要がある為です。
if prevRev, ok := path2Revision[rev.Path]; ok { if prevRev.CreatedAt.Date.After(rev.CreatedAt.Date) { path2Revision[rev.Path] = rev } continue
実はこの場合、prev
自体がBeforeかAfterで考えないといけないという設計らしく、実際はAfter
じゃなくてBefore
でした。
if prevRev, ok := path2Revision[rev.Path]; ok { if prevRev.CreatedAt.Date.Before(rev.CreatedAt.Date) { path2Revision[rev.Path] = rev } continue