開発環境の構築をより簡単にしてくれるツール Devbox

開発環境の構築をより簡単にしてくれるツール [Devbox](https://www.jetpack.io/devbox) について紹介しようと思います

## Devbox とは

Devbox とは jetpack.io がオープンソースで開発している、開発環境構築ツールです
ローカルの環境を汚すことなく、開発用に必要なパッケージがインストールされた状態で Shell を簡単に作成することができます

### Devbox をなぜ利用するのか

Devbox は Docker コンテナや独自の環境を構築・管理するよりも多くの利点があります

#### チームのための一貫した Shell

Devbox の設定ファイルである devbox.json に開発必要なツールを宣言することで、チームメンバー全員が同じ環境で開発をすることができるようになります

#### 環境を汚さず新しいツールを試せる

Devbox はローカルとは分離された環境を提供するので、新しいツールを試してみたいといったときにローカルの環境を汚すことなく試すことができます

#### 実行速度が早い

環境を作成するのに余計な仮想レイヤーを使用しないため、仮想化によるコマンド実行速度の遅延が起きにくいです

#### バージョン違いによるコンフリクトからさよならできる

それぞれバージョンの異なる同じバイナリが必要なプロジェクトを同時に開発していたとしても、それぞれの開発環境をコンフリクトすることなく作成することができます
また、プロジェクトのバージョンごとに検証用の環境を複数用意することもできます


## Devbox でできること

Devbox では以下のことができます

- ローカルとは分離された開発用 shell 環境の作成
- Devbox で作成した開発環境をもとに Dockerfile の出力
- VSCode の devcontainer 機能との連携
- リモートの開発環境と同一のものをローカルに複製


## Devbox を実際につかってみた

以下のコマンドで Devbox をインストールします

```bash
$ curl -fsSL https://get.jetpack.io/devbox | bash
```

開発環境に必要なパッケージをインストールします
今回は ruby 3.1 をインストールしてみます

```bash
$ devbox add ruby_3_1
```

Devbox は [Nix Package Manager](https://nixos.org/download.html) を利用しパッケージのインストールを行うため Nix Package Manger がインストールされていない場合、インストールするかを確認してきます
`y`を入力し`Enter`を押すことで自動でインストールされます
インストール完了後ターミナルを再起動する必要があるので、一度ターミナルを終了さたのち再度実行してください

Shell を起動します
devbox.json に宣言されている内容をもとに開発環境の作成がおこなわれ、ローカルとは分離された Shell が起動します

```bash
$ devbox shell
Starting a devbox shell...
$ ruby --version
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [arm64-darwin22]
```

`exit` で開発環境を抜けてローカルで ruby のバージョンを確認してみると次の結果になりました

```bash
$ ruby --version
ruby 2.7.5p203 (2021-11-24 revision f69aeb8314) [arm64-darwin21]
```

開発環境とローカルでは異なる ruby が実行されることがわかります


最後に Dockerfile を生成してみます

```bash
$ devbox generate dockerfile
```

上記のコマンドを実行すると、以下の内容の Dockerfile が生成されます

```Dockerfile
FROM alpine:3

# Setting up devbox user
ENV DEVBOX_USER=devbox
RUN adduser -h /home/$DEVBOX_USER -D -s /bin/bash $DEVBOX_USER
RUN addgroup sudo
RUN addgroup $DEVBOX_USER sudo
RUN echo " $DEVBOX_USER      ALL=(ALL:ALL) NOPASSWD: ALL" >> /etc/sudoers

# installing dependencies
RUN apk add --no-cache bash binutils git libstdc++ xz sudo

USER $DEVBOX_USER

# installing devbox
RUN wget --quiet --output-document=/dev/stdout https://get.jetpack.io/devbox | bash -s -- -f
RUN chown -R "${DEVBOX_USER}:${DEVBOX_USER}" /usr/local/bin/devbox

# nix installer script
RUN wget --quiet --output-document=/dev/stdout https://nixos.org/nix/install | sh -s -- --no-daemon
RUN . ~/.nix-profile/etc/profile.d/nix.sh
# updating PATH
ENV PATH="/home/${DEVBOX_USER}/.nix-profile/bin:/home/${DEVBOX_USER}/.devbox/nix/profile/default/bin:${PATH}"

WORKDIR /code
COPY devbox.json devbox.json
RUN devbox shell -- echo "Installing packages"
ENTRYPOINT ["devbox"]
CMD ['shell']
```

Dockerfile 内でも Devbox を使用して環境構築を行っているので、全く同じ環境が作成されることがわかります。

また、Node.js おける package.json のようにスクリプトを定義することもできます。

```json:devbox.json
{
  "packages": [
    "rustup",
    "wasmtime",
    "libiconv"
  ],
  "shell": {
    "init_hook": [
      "projectDir=$(pwd)",
      "rustupHomeDir=\"$projectDir\"/.rustup",
      "mkdir -p $rustupHomeDir",
      "export RUSTUP_HOME=$rustupHomeDir",
      "export LIBRARY_PATH=$LIBRARY_PATH:\"$projectDir/nix/profile/default/lib\"",
      "rustup default stable",
      "rustup target add wasm32-wasi",
      "cargo fetch"
    ],
    "scripts": {
      "build": "cargo rustc --target wasm32-wasi",
      "build-docs": "cargo doc",
      "clean": [
        "cargo clean"
      ],
      "start": "cargo run",
      "test": "cargo test -- --show-output"
    }
  },
  "nixpkgs": {
    "commit": "52e3e80afff4b16ccb7c52e9f0f5220552f03d04"
  }
}
```

設定ファイルである `devbox.json` に上にあるような記述をすることで、`devbox run <script_name>` で `scripts` に定義されたスクリプトを実行することができます。

`init_hook` は `devbox shell` を実行したときや、`devbox run start` などのスクリプトを実行した際に必ず実行される処理です。
この設定の場合、必ず rustup の設定や環境変数の設定が行われる様になっています。

## 終わりに

Devbox を使用することで簡単に開発環境を用意できることがわかりました
Docker を使用して開発環境を用意しようとすると、特に mac ではボリュームのマウントがボトルネックになり開発効率に問題がでるということがおおいので、ネイティブにプロジェクトそれぞれの開発環境を用意できるのはとても素晴らしいと感じました