レンタルサーバーにGitHub Actionsで自動デプロイ（rsyncするだけのはずが…）

## 動機
GitHubで管理、開発したコードを自動でレンタルサーバーにアップロードすると、かっこいい！（あと便利）
rsync叩くだけだから簡単やろ！w
　→ 結果：数時間の沼にハマる(´；ω；`)

コピペできるような、ピンとくる記事がなかったので知見を共有します。

### 環境

レンタルサーバー：[ColorfulBox](https://www.colorfulbox.jp/)（プランはBox1）
GitHub：適当なプライベートリポジトリ

### 免責
「日曜プログラマー」を自称している素人がゆるりと勉強した内容です。
内容を理解したうえで、自己責任でご使用ください。

記事中のコードはパブリックドメインとしてご自由にお使いください。

## TL;DR

`rsync --rsync-path=...`

https://gist.github.com/kokko-san/836bd530dc17c886446d5b2f85714b9f

## セットアップ

### 仕様
`develop`ブランチにpushされたら、実行する
　→ `develop`ブランチの内容を取得する
　→ サーバーへssh接続経由でrsyncコマンドを使い、`/path/to/www-root/`の内容を更新

### 事前準備
今回使用したColorfulBoxにはrsyncコマンドがインストールされていなかったので、インストールします。
しかし、root権限は与えられていないので`sudo apt install rsync`など到底できません。かなしい。

そこで、

1. サーバー上にソースコードを用意
2. サーバー上でコンパイル
3. コンパイル先にパスを通す

という作業を踏む必要があります。
依存関係の準備に手間取った気がしますが、基本的にはコマンドをたたくだけです。

こちらのサイトが新しくかつ詳しく、非常に参考になりました！
[ColorfulBox に Rsync をインストールする - Atuweb](https://atuweb.net/201902_colorfulbox-install-rsync/)

### workflowを作成

以下のファイルをリポジトリの`/.github/workflows/`に追加します。

```yml:deploy2dev.yml
name: deploy to development env.
 
on:
  push:
    branches:
      - develop
 
jobs:
  deploy:
    runs-on: ubuntu-latest
 
    steps:
      - uses: actions/checkout@v2
        with:
          ref: develop

      - name: prepare ssh
        run: |
          touch /tmp/key && echo "$SSH_KEY" > /tmp/key
          chmod 700 /tmp/key
          touch /tmp/known_hosts && echo "$KNOWN_HOSTS" > /tmp/known_hosts
        env:
          SSH_KEY: ${{ secrets.SSH_KEY }}
          KNOWN_HOSTS: ${{ secrets.KNOWN_HOSTS }}

      - name: rsync deploy
        run: |
          rsync -rptgDvz --delete \
          -e "ssh -i /tmp/key -o UserKnownHostsFile=/tmp/known_hosts" \
          --rsync-path=~/.local/bin/rsync \
          --exclude='/.git/' --exclude='/.github/' \
          $GITHUB_WORKSPACE/ $USERNAME@$SERVER_ADDRESS:$DEVELOP_SERVER_DESTINATION
        env:
          DEVELOP_SERVER_DESTINATION: ${{ secrets.DEVELOP_SERVER_DESTINATION }}
          SERVER_ADDRESS: ${{ secrets.SERVER_ADDRESS }}
          USERNAME: ${{ secrets.USERNAME }}
```

こちらにも上げています。
https://gist.github.com/kokko-san/836bd530dc17c886446d5b2f85714b9f

### secretを用意
`https://github.com/[user or org name]/[repo name]/settings/secrets/actions`に

<dl>
  <dt>SSH_KEY</dt>
  <dd>SSH用の秘密鍵</dd>
  <dt>KNOWN_HOSTS</dt>
  <dd>接続先の、想定されるknown host情報（詳しくは後述）</dd>
  <dt>DEVELOP_SERVER_DESTINATION</dt>
  <dd>デプロイ先のパス。e.g. /path/to/www-root/</dd>
  <dt>SERVER_ADDRESS</dt>
  <dd>SSH接続先のアドレス。e.g. example.com</dd>
  <dt>USERNAME</dt>
  <dd>SSHでログインするユーザー名</dd>
</dl>

を用意します。

このような感じになります↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273995/b6d207ab-c35f-d5b4-2208-2fe296d3a9f8.png)

### 完了！
これで、あとは`develop`ブランチにpushすれば自動的にアップロードが始まるはずです。
`main`ブランチで動かしたいなどの応用も簡単に効くでしょう。

---

## 沼ポイントの解説

### checkout
Git弱勢なのでcheckoutって何に使うねんという感じでしたが、これは「指定のリポジトリ（の指定のブランチ、コミットetc.）をcheckout（≒clone）してくれるaction」みたいです。

actionを実行し始めた段階では、新しくコンテナが展開されただけの状態なので、actionを登録しているリポジトリのファイルすら持っていません。
そこで、checkoutしてリポジトリの情報をとってきます。
（応用編として、他のリポジトリの情報もとってこれるようですが）

actionを登録しているリポジトリがプライベートリポジトリでも、checkoutするときに認可が必要ないのが今回の使用方法でのポイントでしょうか。

### known_hosts
rsyncするときの`-e`引数を、

```
-e "ssh -i /tmp/key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
```

という風に`-o StrictHostKeyChecking=no`を指定すれば、known_hostsを用意せずとも接続はできます。
ただ、デフォルトでaskなものをわざわざnoにするのは少し気持ちが悪いので、きちんと用意することにしました。

手元のbash環境で（今回はWSLのUbuntuを用いた）以下ように`ssh-keyscan`をして、

```
username@COMPUTER-NAME:~# ssh-keyscan -H example.com
# example.com:22 SSH-*.*-OpenSSH_*.*
|1|***...
```

出てきた出力すべてをリポジトリのSecretとしてコピペすれば多分よいでしょう。

非常に参考になりました↓

https://qiita.com/kawaz/items/20983ec286088a1ae5c7

### `--rsync-path`
こいつです。今回の主役 兼 悪役 兼 ヒーローです。


そもそもですよ、世の中には煩雑なrsyncのオプションやssh鍵の準備を自動化してくれるactionのリポジトリがあります。
e.g. [Rsync Deployments Action](https://github.com/marketplace/actions/rsync-deployments-action)

これを使えばカンタンじゃん！！！
と思ったはいいものの、実際に使ってみるとなぜか

```
bash: rsync: command not found
```

とログに残して死んでいきます。

おっかしいなぁと、自分でrsyncをたたくworkflowを用意しても`command not found`。

手元でサーバーにsshして`which rsync`はちゃんと動くのに！なんで！！
と悩み続け、きちんとrsyncのお勉強をしたら出てきました。`--rsync-path`オプション。

このオプションは、 ***「rsyncへのパスがローカルとリモートで異なる場合、リモートでのパスを指定するオプション」*** です。

サーバーで`which rsync`をたたいて出たパスを、`--rsync-path=`で指定するだけです。

正直、このオプションの存在を知らしめたいがためにこの記事を書いています。

## 改良できそうなポイント
### `--exclude`というか、`--exclude-from`というか
rsyncの`--exclude`に現在はどのリポジトリでもいらないであろう、`/.git/`と`/.github/`ディレクトリを指定していますが、実際にはさらに`README.md`などもっといらないファイルがあると思います。

今回のようにwebサーバー上に置く場合は`.htaccess`を使って`deny from all`もいい気がしますが、リポジトリに`rsync-excludes.txt`を用意してrsyncに`--exclude-from`で指定するのもきれいな気がします。


### `--rsync-path`の自動設定
これってssh接続して`which rsync`した内容を自動で設定すればもっとスマートなのでは？
未検証なので試してみたいと思います。

---

記事は以上です！
LGTMやコメント、編集リクエストなどお待ちしています！