結局 VSCode の設定ファイルはどこまでリポジトリに入れるべきなのか

表題の件について私見を述べていきます。あくまでも私見です。

## そもそもエディタや IDE 固有の設定ファイルをリポジトリに入れるべきなのか

個人的には用法をしっかりと守った上でなら入れてしまって良いと思っています。
入れたところで他のエディタや IDE の害になるわけではないですし、ファイルサイズも知れたものなのであまりデメリットはないように思います。
逆に入れないことでチームメンバーが各々似たような設定をローカルで書くことになってしまうのは労力の無駄です。
特に同じエディタ・IDE を使用している人が多いチームではなるべく設定ファイルを共有してしまった方が良いのではないでしょうか。

## エディタ・IDE 設定をリポジトリに入れるときに守るべき原則

基本的には「チームの利益になるものだけを入れろ」ということになるかと思います。
「個人のための設定は入れるな」と言い換えることもできます。
他の開発者(チームメンバー)の開発体験を損なわないように注意することが重要です。

## github/gitignore を見てみる

[github/gitignore](https://github.com/github/gitignore) はエディタやフレームワークなどに応じた様々な `.gitignore` テンプレートが置いてあるリポジトリです。
参考としてこのリポジトリの [VSCode 用の `.gitignore`](https://github.com/github/gitignore/blob/master/Global/VisualStudioCode.gitignore) を見てみましょう。

2020年9月現在のものを以下に転記します。

```gitignore:Global/VisualStudioCode.gitignore
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace

# Local History for Visual Studio Code
.history/
```

## 結局何をリポジトリに入れるべきか

ファイルごとにリポジトリに入れるべきかどうかとその理由、入れる際の注意点やテクニックなどを紹介していきます。
「なるべくリポジトリで設定を共有しつつ、開発者各々のローカル環境でカスタマイズできるようにする」という基準で判断しています。
繰り返しますがあくまでも私見です。より良い方法があれば是非教えていただきたいです。

### .vscode/settings.json

基本的にはリポジトリに入れてしまって良いと思います。ただし、リポジトリに入れる場合は共有すべき最低限の設定のみ記述するように注意する必要があります。

使用する linter や formatter の設定などは、全員が同じツールを同じ設定で使うべきなので「共有すべき設定」に当たるでしょう。

```json:.vscode/settings.json(良い例)
{
  "go.lintTool": "golangci-lint",
  "go.lintFlags": ["--fast"]
}
```

UI 関連の設定や、lint/format を自動実行するタイミングの設定などは人によって好みが別れるので共有すべきではありません。
また、環境依存の設定も当然共有すべきではない(というかできない)でしょう。
これらの設定はユーザー設定に書くべきです。

```json:.vscode/settings.json(悪い例)
{
  // UI 関連の設定は個人の好みなので共有すべきではない
  "window.zoomLevel": 1,
  "terminal.integrated.fontSize": 15,

  // lint や format の自動実行は個人の好みなので共有すべきではない
  // エディタ設定で強制するのではなく別途 CI でチェックすべき
  "go.lintOnSave": true,
  "editor.formatOnSave": true,
 
  // 個人の環境に依存する設定は当然共有すべきではない (できない)
  "go.gopath": "/home/frozenbonito/go"
}
```

ちょっと話が逸れますが、文字コードやインデント関連の設定は `settings.json` に書くより [EditorConfig](https://editorconfig.org/) 設定 (`.editorconfig`) に書いた方が良いでしょう。EditorConfig であれば各種エディタ用のプラグインが出ているので違うエディタを使っている人とも設定を共有することができます。

```json:.vscode/settings.json(EditorConfig案件)
{
  // こういう設定は .editorconfig に書くべきなので
  // リポジトリに入れる settings.json には書かない
  "files.encoding": "utf8",
  "files.eol": "¥n",
  "files.trimTrailingWhitespace": true,
  "files.insertFinalNewline": true,
  "[ruby]": {
    "editor.insertSpaces": true,
    "editor.tabSize": 2
  }
}
```

難しいのはワークスペース内の特定のパスや実行ファイル名を設定に書く必要がある場合です。
ワークスペース内のファイルの相対パスは個人の環境によらず一定のはずですが、Windows とそれ以外の環境ではパスセパレータや実行ファイル名が異なるといった問題があります。

例えば以下の例では linter の設定ファイルがデフォルトとは異なる場所に配置されているためオプションで明示的に指定する設定を行っていますが、パスセパレータの問題で Windows では動作しません。

```json:.vscode/settings.json(プラットフォーム依存)
{
  "go.lintTool": "golangci-lint",
  // Windows の場合 "${workspaceFolder}¥¥subdir¥¥.golangci.yaml" とする必要がある
  "go.lintFlags": ["--fast", "-c", "${workspaceFolder}/subdir/.golangci.yaml"]
}
```

チームメンバー全員が同一プラットフォームで作業していることが保証できるのであればこの状態でリポジトリに入れてしまっても問題はありませんが、そうでない場合はそのままリポジトリに入れるのは避けた方が良いでしょう。
この場合、次のいずれかの方法で対応することになるかと思います。

- 共有したい設定は `settings.json.default` のような形でリポジトリに入れておき、`settings.json` は `.gitignore` に入れる
  - 実際に使う `settings.json` は `settings.json.default` を参考に各々ローカルで作ってもらう形です
- OS / マシン特有の設定を記述することができる VSCode 拡張の [platform-settings](https://marketplace.visualstudio.com/items?itemName=runarsf.platform-settings) を使用する
  - この方法であれば `platform-settings` の設定を含めた `settings.json` をそのままリポジトリに入れることができます
  - チームメンバー全員に拡張機能を入れてもらう必要があるので、`extensions.json` に `runarsf.platform-settings`に追加しリポジトリに入れると良いでしょう

ちなみに公式で `settings.windows.json` などのファイルでプラットフォームごとに設定を上書きする機能が検討されています[^1]が、2020年9月現在実装には至っていません。

[^1]: https://github.com/microsoft/vscode/issues/5595#issuecomment-565381016

---

ところで、`settings.json` をリポジトリに入れてしまうと「特定のワークスペース（フォルダ）でだけ有効にしたい個人的な設定」を追加することが難しくなります。
具体的には「ユーザー設定で prettier による自動フォーマットを有効にしているが、特定のワークスペースでは競合する他の formatter を使わなければならないので無効化したい」といったケースです。
このような設定を追加したい場合はリポジトリ管理外の `*.code-workspace` に設定を書いてしまうのが良さそうです。

```json:example.code-workspace
{
  "folder": [
    {
      "name": "Root",
      "path": "."
    }
  ],
  "settings": {
    "prettier.enable": false
  }
}
```

### .vscode/tasks.json

タスクは当然共有したいので、リポジトリに入れるべきだと思います。

### .vscode/launch.json

デバッグ設定も共有されていた方が便利なので、リポジトリに入れるべきだと思います。

### .vscode/extensions.json

[公式ドキュメント](https://code.visualstudio.com/docs/editor/extension-gallery#_workspace-recommended-extensions) でも言及されているように、使用すべき VSCode 拡張をチームメンバーと共有するためのファイルなので、当然リポジトリに入れるべきです。

### *.code-workspace

リポジトリに入れるべき場合とそうでない場合があります。
複数のリポジトリをまとめるためのワークスペース設定の場合は、開発者のローカルのディレクトリ構成に依存してしまうので共有は難しいでしょう。
1つのリポジトリに複数のプロジェクトが存在するような場合(モノレポ構成の場合)は、各プロジェクトの相対パスを `folders` にまとめた `*.code-workspace` がリポジトリに入っていると便利です。

[.vscode/settings.json](#vscodesettingsjson) の項目で触れたように、`*.code-workspace` は個人用の設定を書くためにも有用です。基本的には `.gitignore` に入れつつ、必要に応じてデフォルトの設定のみリポジトリに入れる運用が良さそうです。

### .devcontainer/Dockerfile

開発環境が Dockerfile の形で共有できるのは大きなメリットなので当然リポジトリに入れるべきです。

リポジトリに入れる Dockerfile は誰にとっても使える汎用的なものである必要があります。
`ARG` 命令を使うことでビルド時にある程度イメージをカスタマイズできるようにしておくと良いでしょう。[公式](https://github.com/microsoft/vscode-dev-containers) の Dockerfile がとても参考になります。
また、`.bashrc` などの個人向けの設定ファイルはホストからマウントするか [dotfiles のサポート機能](https://code.visualstudio.com/docs/remote/containers#_personalizing-with-dotfile-repositories) を使うことでイメージを汚さずに済みます。

### .devcontainer/devcontainer.json

個人的には `.gitignore` に入れてリポジトリ管理から外すべきではないかと思っています。
`devcontainer.json` には以下のように開発者の作業環境や好みに左右される設定が多く存在するためです。

- ポートを公開する際のホスト側のポート番号
- ターミナルで使用する shell の種類
- イメージのビルド時に与えるビルド変数
  - 特にホスト OS が linux でコンテナ内で non-root ユーザを使用する場合の `USER_UID` や `USER_GID`
- etc.

デフォルトの設定を `devcontainer.json.default` のような形でリポジトリに入れておき、それを参考に各々ローカルで作成するという形が良さそうです。

ちなみにこの `.default` サフィックスによるアプローチですが、個人的にはローカルで1ステップ踏まないと開発を開始できない (デフォルト設定のままで十分な場合でも！) という面であまり好きではありません。
リポジトリはデフォルト設定でよければそのまま使えて、必要に応じて (gitignore された) 別ファイルで設定を上書きできるという状態が理想です。
まさに [上記のような機能をリクエストする issue](https://github.com/microsoft/vscode-remote-release/issues/3279) が作成されているので、今後に期待したいところです。

### .devcontainer/docker-compose.yml

docker-compose を使う場合、当然リポジトリに入れておきたいところです。
問題は docker-compose を使わない場合は `devcontainer.json` に記述していたポートの公開設定やビルド変数を `docker-compose.yml` に記述する必要がある点です。
個人向けの設定も `docker-compose.yml` に書かなくてはならないとなるとリポジトリには入れにくくなります。

回避策として、最低限の `docker-compose.yml` をリポジトリに入れておき、個人向けの設定が必要になった場合は `.gitignore` に追加している別の設定ファイル (例えば `docker-compose.override.yml`) で上書きするようにすると良いでしょう。
`devcontainer.json` の `dockerComposeFile` は複数のファイル名を配列で与えることで、後のファイルで前のファイルを上書きすることができます(`docker-compose` コマンドに複数 `-f` を与えるイメージ)。

```json:.devcontainer/devcontainer.json
{
  // ...

  // docker-compose.override.yml は .gitignore に追加しておき、
  // 必要に応じてローカルで作成・追加する
  "dockerComposeFile": ["docker-compose.yml", "docker-compose.override.yml"],

  // ...  
}
```

(ちょっと使い方は違いますが) [公式ドキュメントに記載されている例](https://code.visualstudio.com/docs/remote/create-dev-container#_extend-your-docker-compose-file-for-development) も参考にしてください。

## まとめ

以上の内容を `.gitignore` に落とし込むと以下のような感じです(一例です)。

```gitignore:.gitignore
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
!default.code-workspace
.devcontainer/devcontainer.json
.devcontainer/docker-compose.override.yml
```