VSCodeのタスク機能でRails Serverをコマンド一発で起動する

[Visual Studio Code](https://code.visualstudio.com/)にはタスク機能が存在し、テストやLintといったツールをVSCodeから実行できます。
npm等ではプロジェクトから自動でタスクを検出したりできるようですが、今回はRuby on Railsのサーバ起動タスクを手動で定義してみました。

## 環境

- Ruby 2.3.1
- Ruby on Rails 5.2.0
- Visual Studio Code 1.24.0
- Windows 10 Pro 17133.1 (April 2018 Update)
- Windows Subsystem for Linux

```
$ cat /etc/os-release | grep VERSION=
VERSION="16.04.4 LTS (Xenial Xerus)"
```

## VSCodeタスクの定義
まずはやってみましょう。

1. VSCodeを起動し、`Ctrl+Shift+P`でコマンドパレットを開きます
1. `>Tasks: Configure Task`でタスクを構成します
1. `テンプレートからtasks.jsonを生成`を選択します
1. `Others 任意の外部コマンドを実行する例`を選択します

`.vscode/tasks.json`が開き、サンプルとして下記のようなタスクが表示されます。

```json:.vscode/tasks.json
{
  // See https://go.microsoft.com/fwlink/?LinkId=733558
  // for the documentation about the tasks.json format
  "version": "2.0.0",
  "tasks": [
    {
      "label": "echo",
      "type": "shell",
      "command": "echo Hello"
     }
  ]
}

```

とりあえず実行してみましょう。`Ctrl+P`でコマンドパレットを開き、`task echo`を実行し`タスクの出力をスキャンせずに実行`します。

するとシェルコマンド`$ echo Hello`が実行され、ターミナル部分に`Hello`と実行されます。

これを応用し、`bundle install`からdb:migrate、Server立ち上げてlocalhost:3000を開く までを連続で行うタスク`rails start`を定義します。

### Railsを起動するVSCodeタスクの定義
下記を順番に実行するタスクを定義します。

1. `$ bundle install`
1. `$ rails db:migrate`
1. `$ kill -9`で実行中のRailsを停止
1. `$ rails server`
1. `$ explorer.exe`か`$ open`で、 http://localhost:3000 を開く

適当なRailsプロジェクトを開き、`.vscode/tasks.json`に下記を記述します。

```json:.vscode/tasks.json
{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "bundle install",
      "type": "shell",
      "command": "bundle install --path vendor/bundle"
    },
    {
      "label": "rails db:migrate",
      "type": "shell",
      "command": "bundle exec rails db:migrate",
      "dependsOn": [
        "bundle install"
      ],
    },
    {
      "label": "rails server start",
      "type": "shell",
      "command": "bundle exec rails server --daemon",
      "dependsOn": [
        "rails server stop",
        "rails db:migrate"
      ],
    },
    {
      "label": "rails server stop",
      "type": "shell",
      "command": "test -e ./tmp/pids/server.pid && kill -9 `cat ./tmp/pids/server.pid` || true"
    },
    {
      "label": "rails start",
      "type": "shell",
      "command": "open http://localhost:3000",
      "windows": {
        "command": "explorer.exe http://localhost:3000"
      },
      "presentation": {
        "reveal": "always",
        "panel": "shared"
      },
      "group": {
        "kind": "build",
        "isDefault": true
      },
      "dependsOn": [
        "rails server start"
      ],
      "problemMatcher": []
    }
  ]
}

```

`Ctrl+Shift+B`で`rails start`を実行し、Railsが起動して、自動的にブラウザでhttp://localhost:3000 が開いたら成功です。

### 詳細

設定項目|意味
:--|:--
label|タスクの名前。コマンドパレットからこの名前で実行する。
type|`shell`とするとbash cmd PowerShell等のシェルコマンドとして解釈される
command|実行するコマンド
windows|プラットフォームに応じて設定項目を変更できる。今回の例ではコマンドをMacでは`$ open`、Windowsでは`$ explorer.exe`とするために使っている。同様に`linux`と`osx`も設定できる。
presentation.reveal|統合ターミナルにフォーカスを当てるかどうか。`always`、`never`の他に、エラー検出時しか表示しない`silent`があるが、今回はエラーを取り扱わないので使えない
presentation.panel|複数のタスク間でターミナルを共有するかどうか。`shared`で共有し、複数回実行時に同じターミナルを使いまわす。他に`dedicated`と`new`が選択できる。
group.kind|`build`とするとビルドタスク扱いとなり、`Ctrl+Shift+B`で実行できる
group.isDefault|デフォルトタスクとして扱い、選択しなくても`Ctrl+Shift+B`するだけで実行できるようになる
dependsOn|指定したタスクを並列で実行し、すべてが正常終了した場合にこのタスクを実行する
problemMatcher|「今後このタスクの出力をスキャンしない」場合は空配列となる

- `$ rails server`は入力を待ち受けてしまうと次のタスクに進まないので、`--daemon`でデーモンとして実行
- エラーコードが返ると次のタスクに進まないので、`rails server stop`は`(command) || true`で強制的に正常終了

## まとめ
こうして、VSCodeからコマンド一発でRails Serverを起動することができました。

プラットフォーム別の設定などが使えるのは便利なのですが、やりたい事に対してjsonの記述量がちょっと冗長な感じもします。

### 参考
- [Tasks in Visual Studio Code](https://code.visualstudio.com/docs/editor/tasks#vscode)
- [Visual Studio Code: Task 機能のクロスプラットフォーム対応 \- Qiita](https://qiita.com/usagi/items/8b23738777f904795b02#_reference-5dfe7177501fe2a621b0)