個人的備忘録：AWS S3 & CloudFrontへの自動デプロイCI/CDパイプラインをコード分析してみた

## はじめに
開発スピードを上げつつ品質も担保するために、自動化を進める必要がありました。

手作業によるデプロイの手間やエラーを減らすため、このワークフローをチームメンバーの方に作成いただきました。

少し個人的にキャッチアップや学習が必要だと感じたため、コード解析を進めていきます。

## ワークフローの概要
このワークフローは、以下のステップを実行します。

1. **リポジトリのチェックアウト**
2. **Node.jsのセットアップ**
3. **依存関係のキャッシュ**
4. **依存関係のインストール**
5. **ESLintによる静的解析**
6. **TypeScriptの型チェック**
7. **Prettierによるフォーマットチェック**
8. **npm auditによるセキュリティスキャン**
9. **プロジェクトのビルド**
10. **AWSへのデプロイ**

## GitHub Actions ワークフローの詳細

### 1. リポジトリのチェックアウト
```yaml
- name: Checkout repository
  uses: actions/checkout@v4
```
リポジトリの最新コードを取得し、ワークフロー内で使用できるようにする。

### 2. Node.jsのセットアップ
```yaml
- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version-file: "skill-typing-front/.nvmrc"
    cache: "npm"
    cache-dependency-path: "skill-typing-front/package-lock.json"
```
`.nvmrc` に記載された Node.js のバージョンをセットアップし、`npm` のキャッシュを利用してインストールを高速化する。

### 3. 依存関係のキャッシュ
```yaml
- name: Cache node modules
  uses: actions/cache@v4
  with:
    path: skill-typing-front/node_modules
    key: ${{ runner.os }}-node-${{ hashFiles('skill-typing-front/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-
```
`node_modules` をキャッシュし、依存関係のインストールをスキップできるようにする。

### 4. 依存関係のインストール
```yaml
- name: Install dependencies using npm ci
  working-directory: skill-typing-front
  run: npm ci
```
`npm ci` を使用して、`package-lock.json` に基づきクリーンな状態で依存関係をインストールする。

### 5. ESLintによる静的解析
```yaml
- name: Run ESLint for static analysis
  working-directory: skill-typing-front
  run: npm run lint
```
`ESLint` を実行し、コードの静的解析を行い、潜在的なエラーやコードスタイルの問題を検出する。

### 6. TypeScriptの型チェック
```yaml
- name: Run type check
  working-directory: skill-typing-front
  run: npm run typecheck
```
TypeScript の型チェックを実行し、型に関するエラーを検出する。

### 7. Prettierによるフォーマットチェック
```yaml
- name: Run Prettier check
  working-directory: skill-typing-front
  run: npm run format:check
```
`Prettier` を使用して、コードのフォーマットが統一されているかをチェックする。

### 8. npm auditによるセキュリティスキャン
```yaml
- name: Run npm audit for security scan
  working-directory: skill-typing-front
  run: npm audit --audit-level=high
```
`npm audit` を実行し、高リスクのセキュリティ問題がないかをスキャンする。

### 9. プロジェクトのビルド
```yaml
- name: Build project
  working-directory: skill-typing-front
  run: |
    VITE_COGNITO_AUTHORITY="${{ secrets.VITE_COGNITO_AUTHORITY }}" \
    VITE_COGNITO_CLIENT_ID="${{ secrets.VITE_COGNITO_CLIENT_ID }}" \
    VITE_COGNITO_REDIRECT_URI="${{ secrets.VITE_COGNITO_REDIRECT_URI }}" \
    VITE_COGNITO_RESPONSE_TYPE="${{ secrets.VITE_COGNITO_RESPONSE_TYPE }}" \
    VITE_COGNITO_SCOPE="${{ secrets.VITE_COGNITO_SCOPE }}" \
    VITE_COGNITO_LOGOUT_URI="${{ secrets.VITE_COGNITO_LOGOUT_URI }}" \
    VITE_COGNITO_DOMAIN="${{ secrets.VITE_COGNITO_DOMAIN }}" \
    VITE_API_URI="${{ secrets.VITE_API_URI }}" \
    npm run build
```
環境変数を設定し、フロントエンドのビルド (`npm run build`) を実行する。

### 10. AWSへのデプロイ

#### **AWS認証情報の設定**
```yaml
- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v4
  with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    aws-region: ap-northeast-1
```
AWS CLI コマンドを使用するために、認証情報を設定する。

#### **S3へのデプロイ**
```yaml
- name: Deploy to S3
  working-directory: skill-typing-front
  run: aws s3 sync build s3://${{ secrets.S3_BUCKET_NAME }} --delete
```
ビルド成果物 (`build` フォルダ) を S3 にアップロードし、不要なファイルを削除する。

#### **CloudFrontキャッシュの無効化**
```yaml
- name: Invalidate CloudFront cache (Optional)
  run: |
    aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} --paths "/*"
```
CloudFront のキャッシュを無効化し、最新のコンテンツをすぐに反映させる。

## 実際に作成したコード

```.github/workflows/deploy.yml
name: Build, Lint, Typecheck, Format, Audit, and Deploy

on:
  push:
    branches:
      - develop

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version-file: "skill-typing-front/.nvmrc"
          cache: "npm"
          cache-dependency-path: "skill-typing-front/package-lock.json"

      - name: Cache node modules
        uses: actions/cache@v4
        with:
          path: skill-typing-front/node_modules
          key: ${{ runner.os }}-node-${{ hashFiles('skill-typing-front/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install dependencies using npm ci
        working-directory: skill-typing-front
        run: npm ci

      - name: Run ESLint for static analysis
        working-directory: skill-typing-front
        run: npm run lint

      - name: Run type check
        working-directory: skill-typing-front
        run: npm run typecheck

      - name: Run Prettier check
        working-directory: skill-typing-front
        run: npm run format:check

      - name: Run npm audit for security scan
        working-directory: skill-typing-front
        run: npm audit --audit-level=high

      - name: Build project
        working-directory: skill-typing-front
        run: |
          VITE_COGNITO_AUTHORITY="${{ secrets.VITE_COGNITO_AUTHORITY }}" \
          VITE_COGNITO_CLIENT_ID="${{ secrets.VITE_COGNITO_CLIENT_ID }}" \
          VITE_COGNITO_REDIRECT_URI="${{ secrets.VITE_COGNITO_REDIRECT_URI }}" \
          VITE_COGNITO_RESPONSE_TYPE="${{ secrets.VITE_COGNITO_RESPONSE_TYPE }}" \
          VITE_COGNITO_SCOPE="${{ secrets.VITE_COGNITO_SCOPE }}" \
          VITE_COGNITO_LOGOUT_URI="${{ secrets.VITE_COGNITO_LOGOUT_URI }}" \
          VITE_COGNITO_DOMAIN="${{ secrets.VITE_COGNITO_DOMAIN }}" \
          VITE_API_URI="${{ secrets.VITE_API_URI }}" \
          npm run build

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      - name: Deploy to S3
        working-directory: skill-typing-front
        run: aws s3 sync build s3://${{ secrets.S3_BUCKET_NAME }} --delete

      - name: Invalidate CloudFront cache (Optional)
        run: |
          aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} --paths "/*"
```

## まとめ
このワークフローにより、`develop` ブランチへのプッシュ時に、コード品質のチェックやビルドが自動で実行されます。

:::note info
**AWSデプロイを有効化することで、本番環境へのデプロイも自動化可能です。**
:::

CI/CDの整備により、開発スピードと品質向上の両立を実現できます。