Scratchのプロジェクトを履歴管理する

## はじめに
Scratchで履歴管理をできるchrome拡張を作ってみました。その備忘録です。

### CoderDojoについて
私は[CoderDojo香椎](https://coderdojo-kashii.connpass.com/)の主催者として活動しています。CoderDojoでは子供たちがScratchやMinecraftなどでデジタル作品を作っています。[^1]
昨年、CoderDojo香椎に参加してくれている中学生がScratchを使った作品でコンテストで賞を取りました。[^2][^3]

子供たちの作った作品を見るのも楽しいんですが、自分でもScratchでどこまでできるのか試してみたくなり、作品を作ってみました。
[ネゴ](https://koncent.jp/view/item/000000001904)というボードゲームをScratchで再現したものです。

https://scratch.mit.edu/projects/957446057/

### Scratchを使ってみて

初めて本格的に触ってみるといくつか不便なところがありました。
例えば、Scratchで変数を作成するときは、「すべてのスプライト用」と「このスプライト用」というスコープが選択できます。しかし、作成中に変数のスコープを変えたくなっても変更することはできません。そのためスコープを変更したくなったら、もう１つスコープを変えた変数を作り、参照している変数を１つづつ置き換えるというやり方で対応していました。

### Scratch Addons
Scratchに詳しい方からScratch AddonsというChrome拡張機能を教えてもらいました。[^4]
Scratch Addonsには色々と改良することができ、変数のスコープを後から変更することもできるようになります。
特にデバッグ用のログ出力を追加する拡張は作品作りに重宝しました。

### 修正履歴を管理したい

作品の規模が大きくなると以前に修正したコードに戻したくなることがありました。[^5]いわゆる履歴管理を行いたいのですが、さすがにScratch Addonにもそのような機能はありませんでした。

Scratchには「コンピューターから読み込む」、「コンピューターに保存する」というメニューがあり、ファイルのimport/exportはできます。
出力したファイルを履歴管理できるストレージに保存すれば、やりたいことを実現できそうなきがしました。

## ScratchHubの実装
Scratch Addonsのようにchrome拡張で実装することにしました。

### 実現したいこと

chrome拡張の実装をしたことはないので、実現したいことをまとめて１つずつ調査していきました。chrome拡張で実現したいことを列挙します。

- Scratchのプロジェクトファイルを拡張機能で取得する
- 取得したファイルをgithubにアップロード(push）する
- プロジェクトファイルの変更履歴を表示する
- 変更履歴からプロジェクトファイルをダウンロードする（もしくはScratchにそのまま反映する）


### Scratchのプロジェクトファイルを拡張機能で取得する
#### Scratchのプロジェクトファイル

「コンピュータに保存する」というメニューからsb3ファイルが取得できます。sb3はzipファイルなので拡張子をリネームすれば、普通に解凍できます。

解凍すると下記のようにjsonファイルと素材用のファイルが格納されています。
project.jsonにはコードブロックの定義や素材ファイルのIDなどが記載されています。

```
<作品タイトル>
├ project.json
├ xxx.svg
├ yyy.svg
├ zzz.wave
└ www.wave
```
#### プロジェクトファイルをAPIで取得する
Scratch APIに関するまとまった情報を見つけられなかったので、試行錯誤しつつ調べていきました。[^6][^7]
調査の結果、sb3ファイルを取得するには下記の手順が必要でした。
1. api.scratch.mit.edu/projects/<projectId> からtoken情報を取得
2. projects.scratch.mit.edu/<projectId>/?token=<token>でproject.jsonを取得
3. project.jsonを読み込み、md5extを指定してassetsファイルを取得する。assets.scratch.mit.edu/internalapi/asset/<md5ext>/get/
4. 全てのファイルを取得し、zipとして圧縮する。

### 取得したファイルをgithubにアップロード(push）する
#### chrome拡張でgithubを使う
github APIを利用したchrome拡張を見つけたので、[こちら](https://github.com/matsubara0507/bookmark-hub)をもとに実装しました。

https://matsubara0507.github.io/posts/2017-06-06-make-chrome-extension-bookmark-hub.html

#### github APIを使う
取得したsb3ファイルを、github APIを使って<projectId>/project.sb3のパスでpushします。

参考にしたソースを利用して、githubへのログイン、リポジトリの取得及び保存は実現できました。

githubへのpushはproject.sb3ファイルの1つのみなので、[Contents API](https://docs.github.com/ja/rest/repos/contents?apiVersion=2022-11-28#create-or-update-file-contents)を使用しました。

### プロジェクトファイルの変更履歴を表示する
[Commits API]
(https://docs.github.com/ja/rest/commits/commits?apiVersion=2022-11-28#list-commits) でcommitsを取得することができます。取得条件にpathがあるので<projectId>/project.sb3を指定します。

### 変更履歴からプロジェクトファイルをダウンロードする
[contents API](https://docs.github.com/ja/rest/repos/contents?apiVersion=2022-11-28#get-repository-content) の条件にpathとrefがあるので、対象のファイルとコミットIDを指定することができます。

ファイルをダウンロードするときは、Sctrachのプロジェクト名をファイル名に変更しておきます。「コンピューターに読み込む」を実行したとき、<ファイル名>.sb3のファイル名がScratchのプロジェクト名に反映されます。

<details><summary>Scratchにそのまま反映できるか</summary>

sb3ファイルをダウンロードせず、そのままScratchに反映させたかったのですが、実現できそうなAPIが無さそうだった。

[このあたり](https://github.com/scratchfoundation/scratch-gui/blob/develop/src/lib/sb-file-uploader-hoc.jsx)の処理をchrome拡張側から実行することができれば実現できるかもしれない。
</details>

## おわりに
実装したソースです。

https://github.com/u-akira/scratch-hub

### 差分表示について
蛇足ですが、差分表示について考えたことをまとめておきます。

修正履歴を知りたいということは、当然のことながら差分の表示もさせたいと思っていました。始めはsb3ファイルを管理するのではなく、project.jsonを管理すればgithub上で差分表示ができるのではないかとも考えました。（連携対象をgithubにした理由でもあります。）

もしくはgit diffの結果を取得すれば、差分情報をどうにかして表現できないかという構想がありました。
ただ、実装を進めているうちにビジュアルプログラミングであるScratchの「テキストの差分」を取得したとしても有益な情報は得られないと考えなおしました。project.jsonの履歴管理は筋が悪そうな気がしてきました。

Scratchの差分表示についてはもう少し考えて、何かしらの答えを出そうと思っています。



[^1]: https://coderdojo.jp/
[^2]: https://u22procon.com/result/ 経済産業大臣賞（アイデア）です。
[^3]: https://scratch.mit.edu/projects/1065305056/
[^4]: https://scratchaddons.com/ja/
[^5]: https://scratch.mit.edu/projects/957446057/ では約16000個のブロックが使われています。
[^6]: https://scratch.mit.edu/discuss/topic/655936/?page=1#post-6906036 ここのコメントが分かりやすかったです。
[^7]: https://github.com/itta611/itta611.github.io/ sb3ファイルの取得方法はこちらを参考にしました。