Box CLI を使ってプロジェクトのBoxへのアップロードを自動化する

# はじめに
Boxにプロジェクトをアップロードするときいちいちブラウザを開いてドラッグアンドドロップするのは面倒くさいですよね。面倒くささからアップロードする頻度が落ちて思わぬ事故により大幅なロールバックを食らうこともあるかもしれません。しかし, Box CLIを使って自動化すればコマンドを一つ叩くだけで簡単に同期ができます。やりましょう。
# 前提
OSはLinuxを使うことを前提としています。Macでもできそうな気はしますが, 手元に環境がないので動作確認はしていません。（よく知りませんが, Windowsでもgit bashとかを使えばできるのかな？）

ファイル, フォルダ名に空白が入っているとエラーがでると思います。

また, jqを使うのでインストールしておいてください。

# 注意
自分が使うために作ったスクリプトなので, 改善点は思いつくだけでもいくつもあります（面倒くさくて実装してない）。もっと良いアルゴリズムがあってもあまり怒らないでください。

# Box CLIのセットアップ
このサイトの手順に従ってセットアップしましょう。

https://ja.developer.box.com/guides/cli/quick-start/

# プロジェクトを置くディレクトリの作成（ローカルとBox）
まずはローカルにプロジェクトを置くディレクトリを作成しましょう。ここではディレクトリ名をtestとします。作成できたらそのディレクトリには入らずに以下のコマンドを叩きます。
```bash
box folders:upload test --id-only
```
すると12桁の数字がコマンドラインに表示されると思います。その数字を box.com/folder/ に続けて書き込み, ブラウザで表示してみましょう。これがBox上にアップロードしたディレクトリとなります。

# プロジェクトフォルダの作成とアップロード
testディレクトリに以下のシェルスクリプトファイルを作成します。名前はmake.shとしてください。ここでparentに代入する文字列には先程urlに打ち込んだ12桁の数字を入れてください。
```bash:make.sh
parent="************"
id=$(box folders:upload $1 -p $parent --id-only)
if [ ${#id} -gt 2 ]; then
    echo $id > "$1/parent"
fi
```
***注意*** シェルスクリプトを新しく作った場合, chmod 755 で実行権限を付与する必要があります。

続いて先ほど作ったディレクトリ内に新しくディレクトリを作成し, これを新規プロジェクトとします。ここではその名前をprojectとします。次はこのprojectをBox上のtestにアップロードします。以下のコマンドを叩いてください。
```bash
./make.sh project
```
するとBox上のtestフォルダの中にprojectフォルダがアップロードされます。また, このスクリプトはproject内にparentというファイルを作成し, アップロードしたプロジェクトのBoxでのIDを格納します。これは絶対に消さないでください。

# プロジェクトのアップロード
`cd project` をしてprojectディレクトリに入り, 以下のシェルスクリプトを作成します。名前はupにしてください。
```bash:up
function look_folders () {
    function get_files () {
            box folders:get $1 --json
    }
    
    for file in $2
    do
        files=$(get_files $1)
        entries=$(echo $files | jq -c .item_collection.entries[])
        file_name=$(echo $file | sed -r 's|.*/||')
        file_flag=0
        id="a"
        for item in $entries
        do
            type=$(echo $item | jq -r .type)
            name=$(echo $item | jq -r .name)
            if [ $file_name = $name ]; then
                if [ $type = "file" ]; then
                    file_flag=1
                    id=$(echo $item | jq -r .id)
                elif [ $type = "folder" ]; then
                    temp_id=$(echo $item | jq -r .id)
                    look_folders $temp_id "$file/*"
                    file_flag="2"
                fi
            fi
        done
        if [ $file_flag = 1 ]; then
            box files:versions:upload $id $file -q
        elif [ $file_flag = 0 ] && [ -d "$file" ]; then
            box folders:upload $file -p $1 -q
        elif [ $file_flag = 0 ]; then
            box files:upload $file -p $1 -q
        fi
    done
}
parent=$(cat parent)
look_folders $parent "*"
```
このスクリプトではフォルダ内のファイルをすべてBoxにアップロードします。project内にフォルダがあった場合, 再帰的に関数を呼び出してそのフォルダ内のファイルをすべてアップロードする仕組みになっています。また, すでにBox上に同じ名前のファイルが存在するときはそのファイルをアップデートするようになっています。試しにフォルダと適当なファイルをいくつか作ってアップロードしてみましょう。

project直下で次のコマンドを叩くとproject内のファイルがすべてアップロードされます。
```bash
./up
```
以上でファイルのアップロードの自動化は終わりです。

# プロジェクトのダウンロード
いつも使っている環境とは別の環境で作業したいときはBoxにあるプロジェクトの最新版をローカルにダウンロードする必要があります。ダウンロードも自動化しておきましょう。以下のシェルスクリプトをproject直下に作ります。名前はdownにしてください。
```bash:down
parent=$(cat parent)
box folders:download $parent --destination ..
```
以下のコマンドを叩くことによってBox上にあるプロジェクトをローカルにダウンロードすることができます。
```bash
./down
```
***注意!!!*** ここではBox上にあるプロジェクトが最新であることを前提としています。Box上にあるプロジェクトがローカルよりも古いバージョンであるときにこのコマンドを実行するとローカルのプロジェクトが古いバージョンにロールバックしてしまうので, 細心の注意を払ってこのコマンドを実行してください。

以上でBoxとの同期の自動化ができるようになります。

# 改善するべき点
+ upにおいてfor内でapiを呼び出してしまっています。apiの呼び出し回数はもっと減らせると思います。
+ アップロードは再帰を使わないでやる方法があればそっちに移行したいです。
+ downにおいてBoxとローカルのどちらが最新なのかを判定するようにできる？
    + gitをお手本としてやれば良い気がします。
+ ローカルで削除したファイル, フォルダをBox上でも削除するスクリプトをupとは独立して追加しようかと思ってます。

# おわりに
これでこまめにBoxにアップロードすることが面倒くさくなくなりました。僕はこの方法でLaTeXのプロジェクトをこまめにアップロードしています。あまり需要のない記事だと思いますが, 改善する方法があれば教えて下さい。（そもそもBoxを使っている人っているんですか？）