electron-builderでS3にアプリケーションの配信をする(Windows)

# はじめに
electron-builderでElectronアプリケーション(Windows)をAWS S3に配信する手順を記載する。

# 前提(ここでは説明しないこと)
- Electronのアプリを作成している
- yarnをインストール済み
- pipをインストール済み
- AWSのアカウントを取得している
- IAMユーザを作成できる権限を持つIAMユーザがある

# 環境
- OS: Windows10
- electron 6.0.10
- electron-builder 21.2.0
- yarn 1.19.0
- pip 18.0

# 手順
## electron-builderをインストール(ローカル)
```bash
yarn add electron-builder --dev
```
参考：[electron-builder](https://www.electron.build/)

package.jsonに以下を追加

```json
{
...
  "scripts": {
    ...
    "pack": "electron-builder --dir",
    "dist": "electron-builder"
  },
  "build": {
    "appId": "your.app.id",
    "files": [
      "dist/**/*",
      "public/**/*"
    ],
    "win": {
      "target": "nsis"
    },
    "nsis": {
      "oneClick": false,
      "allowToChangeInstallationDirectory": true
    }
  },
...
}
```

## S3でバケットを作成する(AWS)
アプリを配布するためのバケットを作成する。

- S3のマネジメントコンソールにアクセス
    - https://s3.console.aws.amazon.com/s3/home?region=ap-northeast-1
- バケットを作成する
    - バケット名: 任意(全世界でユニーク)
        - ここでは`myapp-published`とする(実際はこのバケット名ではない)
- 設定
    - バージョニング: 有効
- アクセス権限
    - パブリックアクセスの設定
        - 以下にチェックを入れる(必要に応じて)
            - 任意のパブリックバケットポリシーを介して、バケットとオブジェクトへのパブリックアクセスとクロスアカウントアクセスをブロックする
            - 新しいパブリックバケットポリシーを介して許可されたバケットとオブジェクトへのパブリックアクセスをブロックする

## S3配布用のIAMポリシー・IAMユーザを作成する(AWS)
### IAMポリシーの作成
S3の特定のバケットにのみアクセスできるポリシー(権限)を作成する。

- IAMのマネジメントコンソールにアクセス
    - https://console.aws.amazon.com/iam/home?region=ap-northeast-1#/home
- ポリシーの作成
    - ポリシー名: 任意
        - ここでは`S3MyappPublishBucketAccess`とする
- JSONを記載する項目に以下を記載

```json
{
  "Version":"2012-10-17",
  "Statement":[
     {
        "Effect":"Allow",
        "Action":[
           "s3:ListBucket"
        ],
        "Resource":"arn:aws:s3:::myapp-published"
     },
     {
        "Effect":"Allow",
        "Action":[
           "s3:PutObject",
        ],
        "Resource":"arn:aws:s3:::myapp-published/*"
     }
  ]
}
```

### IAMユーザの作成
上記で作成したS3MyappPublishBucketAccessポリシーをアタッチしたIAMユーザを作成する。
myapp-publishedバケットにのみアクセスできるIAMユーザになる。

- IAMのマネジメントコンソールにアクセス
    - https://console.aws.amazon.com/iam/home?region=ap-northeast-1#/home
- ユーザー
- ユーザーを追加
    - ユーザー名: 任意
        - ここでは`myapp-publisher`とする
    - プログラムによるアクセス
- 既存ポリシーを直接アタッチ
    - S3MyappPublishBucketAccessポリシーを選択

## AWS CLIのインストール(ローカル)
AWSをコマンドラインから操作できるようにするために、[AWS CLI](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-welcome.html)をインストールする。
AWS CLIのインストールはpipを利用する。

```bash
pip3 install awscli --upgrade --user
```
pipのインストールは以下の記事を参考にした。
[pipのインストール方法](https://qiita.com/suzuki_y/items/3261ffa9b67410803443)

これでawsコマンドが使えます。

## アクセスキーの取得(AWS)
上記で作成したIAMユーザ(myapp-publisher)のアクセスキーを取得する。

- IAMのマネジメントコンソールにアクセス
    - https://console.aws.amazon.com/iam/home?region=ap-northeast-1#/home
- ユーザー
- myapp-publisherを選択
- 認証情報
- アクセスキーの作成
    - 以下の2つの情報が表示される
        - Access key ID
        - Secret access key

## アクセスキーの設定(ローカル)
取得したアクセスキーの設定をする。
aws configureコマンドで設定が可能。

```bash
$ aws configure
AWS Access Key ID [None]: XXXXXXXXXXXXXXXXXXXX
AWS Secret Access Key [None]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Default region name [None]: ap-northeast-1
Default output format [None]:
```

`AWS Access Key ID`にAccess key IDを、`AWS Secret Access Key`にSecret access keyを設定する。

## package.jsonにpublish設定をする
package.jsonのbuildに以下を追加する。

```json
{
  ...
  "build":
   ...
    "publish": {
      "provider": "s3",
      "bucket": "myapp-published"
      "region": "ap-northeast-1"
    }
  }
  ...
}
```

## S3にアプリケーションを配布する
### ビルド

```bash
yarn build
```
### 配信

```bash
yarn dist --publish always
```

# 確認
S3のバケットに以下のファイルが格納されていることを確認。

- myapp Setup 0.0.1.exe
- myapp Setup 0.0.1.exe.blockmap
- latest.yml

無事に配信できました！

# 参考資料
- [electron-builder](https://www.electron.build/)
    - [Publish](https://www.electron.build/configuration/publish)
- [Windows に AWS CLI をインストールする](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-windows.html)
- [Amazon S3: S3 バケットのオブジェクトへの読み取りおよび書き込みアクセスを許可する](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_examples_s3_rw-bucket.html)