TravisCIでGitHub上のファイルをAWS S3へデプロイし、結果をSlackに通知してみた。

# はじめに

AWS S3にHTMLなどの静的ファイルを配置して公開しているWebサイトについて、修正のたびに手作業でデプロイするのが面倒だったので、できるだけ自動化したいと思って取り組みました。

もはや今更感のある内容ですし、公式ドキュメントや先人が残した分かりやすいページなど探せばいくらでもあるんですが、自身の文章化の練習のために敢えて書きました。

内容や記事の書き方など気になる点があれば教えていただけると嬉しいです。

よろしくお願いします。

# どれだけ楽になったか

## 導入前

1. GitHubの開発用ブランチで修正、masterブランチへマージ
1. AWSマネジメントコンソールへログイン、S3の当該バケットに遷移
1. ローカルのファイルをアップロード

導入前は、アップロードすべきファイルを選別したり配置したりといった手作業がとにかく面倒でしたし、しばしば作業ミスも起こしていました。

## 導入後

1. GitHubの開発用ブランチで修正、masterブランチへマージ（変わりなし）
1. TravisCIがmasterブランチの更新を検知し、AWS S3に自動デプロイ
1. TravisCIがSlackへ完了通知を送信

TravisCIがリリース作業を全て肩代わりしてくれるようになったので、masterへマージしたら後は待ってるだけでデプロイが終わるようになりました。

ただ、常に全てのファイルを更新するため、1ファイルだけ修正したい時などは処理が冗長だと感じました。
（TravisCI側で何か設定したらうまく出来るのかも知れないけど、軽く調べた感触では出来なさそうだったのでそのままの状態。）

# 作業内容

作業環境はWindowsです。

## GitHubにTravisCIをインストール

### GitHub上部ナビバーにある[Marketplace]をクリック

![travis_1.png](https://qiita-image-store.s3.amazonaws.com/0/104653/211957ad-50ac-d6eb-3724-0ffa086e1a72.png)

### TravisCIを検索してクリック

![travis_2.png](https://qiita-image-store.s3.amazonaws.com/0/104653/e23b8255-4b32-a56f-7d3e-e977552d4418.png)

### Priceを選択してインストール

（サンプル用なので無料プランを選択しています。）

![travis_3.png](https://qiita-image-store.s3.amazonaws.com/0/104653/0571261e-cedb-0435-f551-30a08566cdb2.png)

### 認証が完了するとTravisCIの管理画面に自動遷移

この後の手順が親切に書かれています。こういうの本当に素敵。

![travis_4.png](https://qiita-image-store.s3.amazonaws.com/0/104653/117d3657-30f0-e50d-a5db-3fa148db094d.png)

### 対象のリポジトリのスイッチをOn

![travis_5.png](https://qiita-image-store.s3.amazonaws.com/0/104653/b075ab08-cfcc-80c0-b3a0-35719bd99580.png)

## AWS S3にデプロイするためのユーザを作成

S3バケットの作成、公開（バケットポリシーの編集）※詳細は割愛
テスト用のS3バケット名は`travistest.s3`としました。

- 本番環境ではRoute53でドメイン取得なども行いました。※割愛

### デプロイ用のIAMポリシーを作成

対象S3バケットに対して必要な権限を与えたIAMポリシーを作成します。
ポリシー名は`travistest.policy`としました。

![iam-policy.png](https://qiita-image-store.s3.amazonaws.com/0/104653/d78f98c0-37b0-4ee5-af3f-98d034e9b080.png)

```json:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "XXX...",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObjectAcl",
                "s3:GetObject",
                "s3:AbortMultipartUpload",
                "s3:DeleteObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::travistest.s3/*"
        }
    ]
}
```

### デプロイ用のIAMユーザを作成

[プログラムによるアクセス]を有効にします。

![iam-user_1.png](https://qiita-image-store.s3.amazonaws.com/0/104653/09a28dd8-80c8-29e2-0f6c-ebbb81b38247.png)

アクセス権限に、先程作成したIAMポリシーを選択します。

![iam-user_2.png](https://qiita-image-store.s3.amazonaws.com/0/104653/74d02a41-f8d9-ecaa-9659-96a423ec6078.png)

IAMユーザを作成したら、Access key IdとSecret Access Keyを忘れずにコピー、またはCSVをダウンロードしておきます。

![access_key.png](https://qiita-image-store.s3.amazonaws.com/0/104653/d4c02326-4d4b-656a-d3ab-94329d0477ba.png)

## .travis.ymlを作成

### 作業環境にtravis.rbをインストール

Gemをインストールします。

```
$ gem install travis
```

### .travis.ymlファイルを生成

リポジトリのルートディレクトリにてtravis.rbを実行します。
今回は、実行時に`setup`オプションを使いました。

```
$ travis setup s3
```

対象のバケット名などを順番に入力していくと、以下のようなファイルが生成されます。
（`setup`オプションを使わずにテキストエディタ等で編集しても可能）

```yml:.travis.yml
deploy:
  provider: s3
  access_key_id: XXXXX...
  secret_access_key:
    secure: XXXXX...
  bucket: travistest.s3
  local-dir: wwwroot
  acl: public_read
  on:
    repo: [GitHub UserName]/[GitHub Repository]
```

### travis.ymlファイルを修正

以下の理由で修正しました。

#### masterブランチが更新された時のみデプロイしたい

```yml:.travis.yml
branches:
  only:
  - master
```

#### Rakeファイルがないって怒られる

デプロイ時に下記エラーが発生しました。

```text:JobLog
$ rake
rake aborted!
No Rakefile found (looking for: rakefile, Rakefile, rakefile.rb, Rakefile.rb)
```

言語設定のデフォルトはRubyなんですかね。
後々`before_deploy`などのコマンドはbashで書きたかったので、Rakeファイルは作成せず、言語にbashを指定することで回避しました。

```yml:.travis.yml
language: bash
```

#### 中間ファイルが削除されないようにする
参考）https://docs.travis-ci.com/user/deployment#Uploading-Files-and-skip_cleanup

```yml:.travis.yml
deploy:
  skip_cleanup: true
```

#### endpoint指定がないって怒られる

デプロイ時に下記エラーが発生しました。

```text:JobLog
The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint. (Aws::S3::Errors::PermanentRedirect)
```

setupオプションではendpointなどが記載されないようなので、テキストエディタで追記しました。

```yml:.travis.yml
deploy:
  endpoint: s3-ap-northeast-1.amazonaws.com
  region: ap-northeast-1
```

#### 最終的な.travis.yml

```yml:.travis.yml
language: bash
deploy:
  provider: s3
  access_key_id: XXXXX.....
  secret_access_key:
    secure: XXXXX....
  endpoint: s3-ap-northeast-1.amazonaws.com
  region: ap-northeast-1
  bucket: travistest.s3
  local-dir: wwwroot
  acl: public_read
  skip_cleanup: true
  on:
    repo: [GitHub UserName]/[GitHub Repository]
branches:
  only:
  - master
```

## 動作確認

masterブランチにPushし、Travis CIの動作を確認します。

![travic_started.png](https://qiita-image-store.s3.amazonaws.com/0/104653/3c86ed83-ffee-e8bc-7493-1d8f87780963.png)

↓

![travis_passed.png](https://qiita-image-store.s3.amazonaws.com/0/104653/1e8f23f0-22eb-dbda-6aa9-82d20fd6826f.png)

無事にデプロイされました。

デプロイの自動化自体はこれで完了ですが、完了したかどうかはTravisCIの管理画面にアクセスしないと分かりません。その手間を省くため、Slackに通知されるようにします。

## TravisCIからSlackにデプロイ完了を通知する

### SlackにTravisCIアプリを追加

（超シンプルなので割愛）

### TravisCIアプリの設定

（画面の指示に従っていくだけなので、改めて書く必要はない気がしますが……。）

投稿するチャネルを選択して[インテグレーションの作成]をクリックすると、セットアップ手順が表示されます。
今回は暗号化の手順を参考にしました。
連携トークンやコマンドは以下からコピーしました。

![slack-travis.png](https://qiita-image-store.s3.amazonaws.com/0/104653/e9073d2a-79c7-3478-2ab1-651dfad7c250.png)

### travis.ymlを編集

.travis.ymlのあるディレクトリにて、travis.rbを実行します。
`encrypt`オプションにより連携トークンを暗号化しつつ、`--add`オプションにより.travis.ymlに追加します。

```
$ travis encrypt "[Slack Team]:[Token]" --add notifications.slack
```

.travis.ymlに追記されました。

```yml:.travis.yml
notifications:
  slack:
    secure: XXX...
```

修正をコミットします。

### 動作確認

masterブランチに修正をプッシュしてみます。

![notification.png](https://qiita-image-store.s3.amazonaws.com/0/104653/5191ccb5-6533-be78-309a-b5990e830391.png)

TravisCIで自動デプロイされた結果がSlackに通知されました。
デプロイが終わったかどうかが、わざわざTravisCIの管理画面にログインしなくても分かるようになりました。

# おわりに

がんばった甲斐あって、その後のリリース作業がとても楽になりました。
修正をmasterにマージしたらSlackに完了通知が来るまで待っているだけなので、一息ついたり、残課題を考えたり、気持ちに余裕が生まれるようになりました。

今更言うまでもなく、作業を自動化することは色々なメリットがあることだと思います。
ただ、この「自動化された仕組みそのもの」には、以下のような問題が出てきます。

 - 最初に設定した人にしか分からない
 - 設定した本人ですら忘れてしまった、など

何をどう設定したのか、必要に応じてドキュメント化して残しておくなど、忘れないための工夫が要ると思いました。今回この記事を書いたのは、それが目的でもあります。

また、今回は知らないことばかりの状態から始めて、色々調べたりエラーにハマったりしながら作ったので、とても時間がかかってしまいました（仕事ではなかったので、誰かにお咎めを受けることはないのですが）。
工数をかけて自動化したところで、将来的に浮く工数と比べればもしかするとマイナスになってしまうかも知れません。今回は自分の勉強だったので気にしませんでしたが、もしこれが仕事だったらそういった面での難しさも出てきただろうなぁと思います。

とはいえ、勉強したことや取り組んだこと自体は必ずプラスになると思っていますし、「未来を楽にするために今頑張ろう」という姿勢は大事にしたいです。