リモートマシンでの実業務開発

※ "リモートワークでの開発"の記事ではありません。

## 動機
うちの会社ではほぼ100%会社貸与のMacBookで開発しています。そんな中で普段の開発のメインマシンがLinux(Ubuntu)なこともあり、「Linuxデスクトップでの実業務開発記をみたいです」とリクエストをもらったので、そのための記事です。
ただ、"Linuxらしい"開発をしているかと言われるとそんなことないので、MacBookな社内で異端児なLinuxユーザの開発の実態を紹介したいと思います。

## リモートマシンでの開発
MacBookを使わないわけではなく、手元のMacBookからリモートのLinuxに入って開発をしています。

### 環境準備
* 手元 = MacBook
    * iTerm (最近terminal appから乗り換えました。)
    * SSH client
    * webブラウザ
    * サブディスプレイ
* リモート = Linuxデスクトップ(Ubuntu)
    * OpenSSH
    * tmux
    * vim
    * 他、開発に必要なもの

IDE？使いません。
マウス？トラックパッド？あまり使いません。
ブラウザ？[Vimium](https://github.com/philc/vimium)を入れましょう。
キーボード&コンソールLoverなみなさんいらっしゃいませ。

### 構成
MacBook本体とサブディスプレイの各ディスプレイにターミナルのウィンドウを用意して、それぞれに複数のタブを開き、そこからsshでリモートマシンにログインというのが基本になっています。

```
   <local>                   <remote>
----------------------------------------
display1                   
  - iTerm:window1
      - :tab1 ssh -------> tmux:session1
      - :tab2 ssh -------> tmux:session2
      ...

display2
  - iTerm:window2
      - :tab1 ssh -------> tmux:session3
      - :tab2 ssh -------> tmux:session4
      ...
```

ローカルはあとはブラウザくらい。
次にリモートのtmuxの内訳。

```
<display1>
tmux:session1 = source code of server application
  - :window1 = server application 1
    - :pane1
      - vim = source code
  - :window2 = server application 1
    - :pane1 = repository management etc...
    - :pane2 = misc, to run script etc...
  - :window3 = docker container management of server application 1
    - :pane1 = docker exec etc...
    - :pane2 = docker run/up (attached)
    
  - :window4 = server application 2
  ...

tmux:session2 = source code of web application (front)
  - :window1 = front application 1
    - :pane1
      - vim = source code
  - :window2 = front application 1
    - :pane1 = repository

  - :window3 = front application 2
    - :pane1
      - vim
  ...

<display2>
tmux:session3 = server application build
  - :window1 = server application 1, gradle etc...
  - :window2 = server application 2
  ...

tmux:session4 = front application build
  - :window1 = front application 1
    - :pane1 = npm etc...
    - :pane2 = misc...
  - :window2 = front application 2
    - :pane1
    ...

```

tmuxって便利ですよね。

現在入っているプロジェクトではバックエンド、フロントエンド共に複数のアプリケーションを開発しているので、そのアプリケーション群をグルーピングしてtmux sessionに割り当てている感じです。開発のメインプロダクトとは別にデプロイやインフラ用に別のリポジトリもあり、それらもまたさらに別のtmux sessionに割り当てています。

* 1 tmux sessionに1~複数のリポジトリ
* ソースコードは1 tmux window = 1 vim
* リポジトリ操作(git)と他コマンド実行用に1 tmux window
* ビルド用に別のtmux session
* 各アプリケーションのビルド用に1 tmux window

ディスプレイ2面体制(MacBook + サブディスプレイ)なので一方はソースコード、他方はビルドというのが基本スタイル。
(webアプリケーションの表示などはport forwardingを活用していますが当記事では省略。)

## ツール群と設定
基本的にターミナル上に生息しているのでIDEのようになんでもイイカンジにまとめて管理してくれるものはありません。なので恩恵を受けているツールや機能を書き残しておきます。
~~なんでもかんでもやってくれすぎるのでIDEって難しいですよね。~~

### [Tmux Resurrect](https://github.com/tmux-plugins/tmux-resurrect)
先に書いた通りtmuxのsessionをたくさん(現在13 sessions)作るので、いちいちsession、window、paneを開いていくのはものすっっっごく面倒なのは言うまでもないでしょう。
Tmux Resurrectはsessionの状態を保存、復元してくれるプラグインで、window内のpaneの分割状態までちゃんと復元されます。ものによっては実行中だったコマンドも復元します。(vimを開いてくれたり、topを実行してくれたり。ありがたやー。)

### Continuous Build
IDEだとコードを書きながらコンパイルエラーをチェック、警告してくれますがピュアなエディタにはそんな機能はありません。(プラグインを入れればできますが入れていません。ビルドすればどうせわかるし。)
そうなると今度はコードを書いて保存したら、都度ビルドコマンドを実行するのが面倒になってきます。ファイルを保存したらビルドして欲しい。

#### `gradle --continuous`
開発中のサーバアプリケーションのビルドツールにgradleを使っています。gradleのオプションには`--continuous`があり、ファイルをウォッチしてタスクを実行してくれる機能があります。上記の構成の`display2:tmux:session3:window*`でこのオプションで実行し、`display1:tmux:session1`でソースコードを書く、というスタイルに落ち着きました。いちいちターミナルを移動してコマンドを叩く煩わしさがこれでなくなります。コンパイルだけなら`gradle --continuous (classes|testClasses)`、テストの実行なら`gradle --continuous test --tests *.FooTest`など。

個人的には、とくにテストの実行においてはこのオプションでの実行はオススメ。理由はgradleの実行時のオーバヘッドがなくなるから。テストはサクサク書いてサクサク実行したいのでそのサイクルが速くなります。IDE(IntelliJ)からだとどうしてもgradle実行直後の"待ち"がありストレスに感じることも。テストを書いていてこの点にストレスを感じている方はお試しあれ。[^1] [^2]
(Run Configurationで`--continuous`をつける方法はやったことあります。もし他にIntelliJでサクサク回す方法をご存知の方がいれば教えてください。)

[^1]: IntelliJはファイルの変更を内部的に管理していて、実際のフィルに変更が適用されるタイミングが"IntelliJ上での保存"時ではないというのがあり(たしか)、IntelliJ上で書きながら裏で`--continuous`というのは食い合わせが悪いことがあります。

[^2]: macOS上ではgradleの`--continuous`オプションは10秒インターバルのポーリングになります。タイミングによっては保存 → タスク再実行まで10秒ほど待ちます。Linuxではファイル保存 → 再実行は瞬時に行われます。

#### [Spring Boot Developer Tools](https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-devtools)
とくにAutomatic Restart。サーバアプリケーションがSpring Boot製なら開発効率が上がります。上記の`--continuous`と併せて

1. `gradle bootRun`
1. 別コンソールで`gradle --continuous classes`

あとはソースコードを編集し保存すると、`classes`で.classが更新され、それをトリガーにサーバがリスタートします。 これもgradleの実行とさらにサーバの立ち上がりのオーバヘッドをかなり減らせます。

#### `jest --watch`
webアプリケーションの場合、jestを使うことも多いと思います。gradleの`--continuous`と同じです。

#### [Chokidar CLI](https://github.com/kimmobrunfeldt/chokidar-cli)
ビルドツール、テストツールによっては上記のようなオプションがないものもあります。そう言った場合にお世話になっているのがこのChokidar CLI。任意のパターンのファイルをウォッチし、任意のコマンドを実行してくれます。npmでglobalにインストールしています。
`chokidar "src/**/*.py" -c "flake8; pytest"`のように、複数のコマンドをまとめて実行もできます。

### アプリケーション内のウィンドウの切り替え
macOSアプリケーションにおいてのウィンドの切り替え設定。上記の通りiTermのウィンドウ(not tmux's windows)を2つで作業をしていますが、`Cmd Tab`ではiTerm内のウィンドウの切り替えができません。そこで
System Preferences > Keyboard > Shortcuts > Move focus to next window
に``Cmd ` ``(backtick)をショートカットを割り当てています。
(キーボードレイアウトがUSなので`Tab`の上が`` ` ``でちょうどいい。)
("next"しかないので1アプリケーションで3つ以上のウィンドウを開いているとサイクリックにしか移動できないのと、ウィンドウ移動後に操作すると"next"が直前に操作していたウィンドウ(最後に操作したウィンドウ順にフォーカスが移動)というのが難点。"previous"があれば``Cmd Shift ` ``ができたのに……。)

## おわりに
というほど何かまとめたいことはありませんが、基本ターミナル上でのみの開発スタイルをご紹介しました。IDEユーザでもコマンドを叩くこともあると思いますが、ターミナル上での作業効率向上のヒントになるものがあれば幸いです。

それではみなさん素敵なターミナルライフを。

---
### 余談
そもそもなんでLinuxというかデスクトップにしたか？
→　MacBookのメモリ(16G)じゃ足りない。containerをどかどか立てるので溢れる溢れる。ブラウザもFirefoxとGoogle Chromeとどちらも使っているのでなお足りない。swap 20Gの記録を打ち立てました。
デスクトップには64G積んだので調子に乗ってcontainerを立てまくっています。メモリが溢れないって素晴らしい！
ついでにintel 10th genの16 logical coresにしたのでさらに快適。

なのでリモートマシンでの開発というスタイルは2020年現在の時世的なアレになる前から。ネットワークがあってsshできればどこでも開発できます。UQモバイルの3日10G超過制限下の1Mbpsでも十分開発できます。

. . .

ターミナルはbackgroundのtransparencyを0.7~0.8くらいにしていて、調べたことを見ながらコードを書く時なんかに、ブラウザがターミナルの裏に行ってもページを見ながら書けるようにしています。読みにくいですが。
当記事執筆時は在宅で仕事をしているのでディスプレイ2面体制と書きましたが、会社の自席では3面体制(MacBook + サブディスプレイ * 2)でやっています。サブディスプレイ2面にターミナル、MacBookにブラウザ。なのでブラウザがターミナルの裏に行くことがないので自宅にももう1面欲しい。(スペースを作れれば。)