React製WebアプリのAWSへのdeploy scriptを作った試行錯誤の過程


システム構成図


システム構成

  • ReactのソースコードはS3 Backetにアップロード
  • BacketはCloudFrontを経由して公開する
  • ステージング環境と本番環境がある

やりたいこと

残念ながらCIは導入されていないので、手動でデプロイする必要がある
ステージング環境と本番環境へのデプロイをできるだけ簡素化したい


これまで手動でやってたこと

  • APIの向き先を変更して npm run build
  • 該当S3バケットにbuildしたファイルをアップロード
  • CloudFrontのキャッシュを無効化
  • これをproductionとstaging で実施

ひとつずつスクリプト化していく!


これまで手動でやってたこと

  • APIの向き先を変更して npm run build
  • 該当S3バケットにbuildしたファイルをアップロード
  • CloudFrontのキャッシュを無効化
  • これをproductionとstaging で実施

Reactのbuild時に.envファイルを切り替える

  • npm-scriptsにprebuildを定義して、そこで.envファイルを差し替え
  • で、postbuildで元に戻す

まぁ 動いた


env-cmd

あとで公式ドキュメント見てたら、そのものズバリの記載があった

Deployment · Create React App
Customizing Environment Variables for Arbitrary Build Environments
(任意のビルド環境用の環境変数のカスタマイズ)

For example, to create a build environment for a staging environment:


どう使う?

package.json
{
  "scripts": {
    "build": "react-scripts build",
    "build:staging": "env-cmd .env.staging npm run build"
  }
}

これまで手動でやってたこと

  • APIの向き先を変更して npm run build
  • 該当S3バケットにbuildしたファイルをアップロード
  • CloudFrontのキャッシュを無効化
  • これをproductionとstaging で実施

Amazon S3へのファイルのアップロード

  • AWS SDKでアップロード
async function putFile(sourceFile) {
  // S3にputするファイル名
  const dir = process.env.DIRECTORY ? `${process.env.DIRECTORY}/` : '';
  const key = sourceFile.replace('build/', dir);
  // ファイルの内容を取得
  const data = await fs.readFile(sourceFile);

  const params = {
    Bucket: process.env.BUCKET,
    Key: key,
    Body: data,
    ContentType: mime.lookup(sourceFile),
  };
  await s3.putObject(params).promise();

  debug(`putObject: ${key}`);
}

これまで手動でやってたこと

  • APIの向き先を変更して npm run build
  • 該当S3バケットにbuildしたファイルをアップロード
  • CloudFrontのキャッシュを無効化
  • これをproductionとstaging で実施

CloudFrontのキャッシュ無効化

deploy.js
const cloudFront = new aws.CloudFront();

await cloudFront.createInvalidation({
  DistributionId: process.env.DistributionId,
  InvalidationBatch: {
    CallerReference: uuid(),
    Paths: {
      Quantity: 1,
      Items: ['/*'],
    }
  }
}).promise();

これまで手動でやってたこと

  • APIの向き先を変更して npm run build
  • 該当S3バケットにbuildしたファイルをアップロード
  • CloudFrontのキャッシュを無効化
  • これをproductionとstaging で実施

環境変数をいい感じに切り替える

dotenv
Dotenv is a zero-dependency module that loads environment variables from a .env file into process.env.

.env.production.env.staging をnpm-scriptsで呼び分ける


どう使う?

deploy.js
// 環境変数TARGETを元に読み込むファイルを切り替え
const target = process.env.TARGET || 'production';
require('dotenv').config({
  path: `${process.cwd()}/.env.${target}`,
});
package.json
{
  "scripts": {
    "deploy": "node scripts/deploy.js",
    "deploy:staging": "TARGET=staging npm run deploy"
  }
}

まとめ

ec2のWebAPI更新が手動なので、これもnpm-scriptsから更新がキックできたらいいな