CircleCIで自動テスト、自動デプロイ入門

本記事はCircle CIでVueプロジェクトの自動テスト実行、AWS S3へ自動デプロイさせるミニマムなCI/CDパイプラインを構築した時の備忘録で、初心者向けの内容です。

## CI/CDとは
CI/CD（継続的インテグレーションと継続的デリバリーまたはデプロイメント）は、ソフトウェア開発において、コードの品質とリリース速度を高めるための自動化プロセス。

これにより、手動での作業や人的エラーが減少し、コードがスムーズにリリースされる環境を構築できます。

- CI: `Continuous Integration`（継続的インティグレーション）
- CD: `Continuous Delivery/Deployment`（継続的デリバリー）

平たく言うと、テスト、ビルド、デプロイのプロセスを自動化する環境のこと。またソースコードのテスト、ビルドを開発者のPC（ローカル）だけで実行してると、その開発者のPCでしかテスト、ビルドが成功しないということになりやすいので、それを防ぐ為にビルド、テストを行う専用の環境をCircleCI等で作った方がいいと言うこと。

`CI=テスト、ビルド`、`CD=デプロイ`なので、CIの先にCDがある。

全てのステージが１本のラインでつながるので、`CI/CDパイプライン`と呼ばれる。


## どうやってCI/CD環境を構築するのか？
CircleCIのような専用ツールを使うのがベスト。

#### CircleCIとは
- 世界3万以上もの企業が使用する人気のCI/CDツール

- CircleCIを利用すればCI/CD環境の維持管理負担の削減や自動テストの開発体験の向上などメリットが多く存在します。無料でビルドが実現するプランも豊富に提供

- CircleCIが人気だが、GitHub Actions、Bitbucket Pipelineなど他にも色々ある


#### CI/CDパイプライン
- CI/CDを実践するために必要な一連のステップ/プロセス/流れ（テスト、ビルド、デプロイ）を自動化したもの

- パイプラインにテストを組み込みんだりも可能

- CI/CD パイプラインは、ソフトウェア開発を自動化する上で一番の土台で、開発のインフラ構築と言える


## 実践編
#### 前提条件
- AWSアカウント、CicrleCIアカウントを作成済み
- VueかReactのプロジェクトを作成し、GitHub等に上げている
- jestなどでテストを実装している（これは必須じゃないです）
    - jest導入は[こちら](https://qiita.com/hisashi_matsui/items/bcbe13116ab0b104c0ea)で記事にしてます

#### 概要
全3ステップの概要です。

![スクリーンショット 2025-06-19 21.58.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/e73593eb-1c99-4558-a99b-71f63f309eda.png)

**全ての起点はレポジトリへのプッシュ**
GitHubへプッシュされる度に、自動でテスト、ビルドが行われ、mainブランチへマージされたらデプロイされるCI/CDパイプライン環境を構築していきます。

### Step 1. CircleCI設定ファイルを作成
このStepでは、CircleCIの設定ファイルを作って、ワークフローの定義をします。
CircleCIはこの設定通りに動いてくれます。

ルート直下に、`.circleci`というディレクトリを作り、その中に`config.yml`というファイルを作ります。

以下はサンプル

```yml
version: 2.1
jobs:
  build-and-test:
    working_directory: ~/project
    docker:
      - image: cimg/node:21.6.0
    steps:
      - checkout
      - run:
          name: Install Dependencies
          command: npm install --legacy-peer-deps
      - save_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
          paths:
            - ./node_modules
      - run:
          name: Run test for the application
          command: npm run test
      - run:
          name: Run build for the application
          command: npm run build
workflows:
  build-and-test:
    jobs:
      - build-and-test
```

設定ファイルには、`jobs`と`workflows`という2つの主な構成要素が含まれます。
- **jobs**： 個々のビルドステップを表し、ビルド、テスト、デプロイといった処理を定義します。ジョブ内で使用する Docker イメージを指定し、必要なコマンド（インストールやテストの実行など）を記述
- **workflows**：ジョブの実行順序や依存関係を定義します。例えば、まずビルドとテストを行い、その結果に基づいてデプロイを行う、という一連の流れを指定

**サンプルでは `steps:` に以下のビルドステップを記述しています**
1. npmインストール
1. キャッシュを保存
1. テスト*を実行
1. ビルドを実行

*本記事のCI/CDにおけるテストはjest等プロジェクト内のテストコードを実行するだけなので、テストコードそのものは自分で実装する必要があります。プロジェクトにテストを導入してなくても問題ないです、testの部分を削除すればOKです。

この修正をレポジトリにプッシュします。（ブランチ切っていたらプルリクをmainにマージしておいてください）

### Step 2. GitHubとCircle CIを繋げる
このStepでは、CircleCIでプロジェクトを作り、GitHubの該当のレポジトリと接続します。
<sup>*初めてCircleCIを触る場合は最初に`Organizations`を作ってください。

1. `Create a project`をクリック
![スクリーンショット 2025-01-29 21.27.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/4b258c5a-8d3b-42ed-a359-b0db1147945e.png)

1. `Build, test, and deploy your software application`を選択
1. プロジェクト名を入力し、`Next`をクリック
1. パイプライン名を入力し、`Next`をクリック
1. プロジェクトを置いているレポジトリを選択（本記事ではGitHub）し、`Next`をクリック
![スクリーンショット 2025-01-29 21.29.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/81de8f59-8d6b-46c8-9ceb-68f998386af3.png)
1. 認証画面に切り替わるので、`Install & Authorize`をクリック
![スクリーンショット 2025-01-29 21.29.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/6757a373-8c3f-439e-9ad0-e9e461f5fc38.png)
1. レポジトリの一覧が出るので、該当のレポジトリを選択し、`Next`をクリック
1. configファイルはレポジトリに既にあるものを使いますという確認。`Next`をクリック
1. CircleCIを動かすトリガーを選択、デフォルトで全てのPushをトリガーにすると言う設定なので、`Next`をクリック
![スクリーンショット 2025-06-16 21.43.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/f729cd2f-fa1a-4175-9ab2-f70aac81d841.png)

1. 最後に設定の概要が出るので問題なければ`Finish setup`をクリックして完了
![スクリーンショット 2025-06-16 21.45.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/062f8046-d935-4280-a853-24a48a0c539e.png)

これによってGitHubのレポジトリの全てのブランチにPushされる度、テストとビルドが実行されます。プルリクエストを作った場合はその結果がプルリクの中に表示されます。

試しにブランチを切って、何か修正してプルリクを作ってみましょう、以下の様にCircle CIの実行結果が表示される様になります。
![スクリーンショット 2025-06-16 21.54.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/5dd1b6dd-51eb-4583-911b-08985a475ccb.png)

意図的にテストが失敗するコードをプッシュしてみましょう、するとCircleCIで失敗した事が表示され、クリックするとCircleCIのページへ遷移して、どのステップで失敗したか詳細を確認できます。
![スクリーンショット 2025-06-16 21.58.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/e50cdd75-81f9-427e-9467-e66c835f4e2d.png)

![スクリーンショット 2025-06-16 21.58.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/c9482ea8-e1e0-45b8-a7d9-6f0d3a335744.png)

<sup>*このプルリクは動作確認用なので削除してOKです。

### Step 3. AWS S3にデプロイ
このStepでは、テスト、ビルドが通ったことを条件に、AWS S3にビルドファイル（本記事の場合、HTML、CSS、JSなど）をデプロイしてアプリケーションを公開します。AWSを沢山触っていきます。

**(1) AWS IAMで、アクセスキーとシークレットアクセスキーを作成**
AWS S3へアクセスする権限の付与なども含めて、[この記事](https://qiita.com/miriwo/items/a513c91eac7186db59fe)がわかりやすいので参考にしてください。

**(2) CircleCI の環境変数設定**
プロジェクト内で使う環境変数に(1)で作成したキーを追加していきます。

プロジェクトのページの右上の `Settings→` ボタンをクリック
![スクリーンショット 2025-06-17 22.43.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/3e2d6b9d-52cb-43b5-829b-2e761e40dc5c.png)

左メニューから`Environment Variables`を選択し、`Add Environment Variables`ボタンをクリックします。
![スクリーンショット 2025-06-17 22.44.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/38d53162-cb06-4bff-8586-07e11da9dfb2.png)

モーダルが開くので、`Name`には以下を使い、
・AWS_ACCESS_KEY
・AWS_SECRET_ACCESS_KEY

`Value`には(1)で取ったアクセスキーとシークレットアクセスキーをセットします。
![スクリーンショット 2025-06-17 22.46.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/5d3c6bd5-d3cc-4205-929e-80ea2369b52c.png)


**(3) AWS S3でバケットを作成**
AWS consoleで`S3`を開きます。
![スクリーンショット 2025-06-17 21.59.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/b174489f-d60d-4703-95b2-061ba2c57866.png)
汎用バケットからバケットを作成をクリック

![スクリーンショット 2025-06-17 22.02.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/ba568db1-3d7f-4943-a0be-541516caca0a.png)
バケット名を入力（ユニークにしないといけないので、自分の名前を入れるといいです）

![スクリーンショット 2025-06-17 22.02.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/270ba1ee-faf8-4e75-b685-fb39d2ba2098.png)
HTMLファイルを置いて、ホスティングするので「パブリックアクセスをすべてブロック」のチェックは外します。またその下のWarningはそれに関する事なので、チェックを入れて承認してください。

**AWS S3とは**
AWS (Amazon Web Services) が提供するオブジェクトストレージサービスです。インターネット上のストレージとして、データの保存、取得、バックアップ、アーカイブなどに利用できます。容量無制限で、高い耐久性と可用性を持つ。HTMLファイルをホスティングすることもできる。

**(4) CircleCI設定ファイルを修正**
`.circleci/config.yml` にいくつか修正を加えます、 (以下参照)

```yml
version: 2.1

executors:
  awscli_container:
    docker:
      - image: amazon/aws-cli:2.15.49
        environment:
          AWS_DEFAULT_REGION: ap-northeast-1
    working_directory: ~/project

  nodejs_container:
    docker:
      - image: cimg/node:21.6.0
    working_directory: ~/project

jobs:
  build-and-test:
    executor: nodejs_container
    steps:
      - checkout
      - restore_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
      - run:
          name: Install Dependencies
          command: npm install --legacy-peer-deps
      - save_cache:
          key: dependency-cache-{{ checksum "package-lock.json" }}
          paths:
            - ./node_modules
      - run:
          name: Run test for the application
          command: npm run test
      - run:
          name: Run build for the application
          command: npm run build
      - persist_to_workspace:
          root: .
          paths:
            - dist  # "dist"ディレクトリがビルド成果物なら

  deploy:
    executor: awscli_container
    environment:
      AWS_S3_BUCKET_NAME: "matsui-cd-test" # 作成したバケット名
    steps:
      - run:
          name: Install tar and gzip
          command: yum install -y tar gzip
      - attach_workspace:
          at: ~/project
      - run:
          name: Deploy to S3
          command: |
            export AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY}
            export AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
            aws s3 sync dist s3://${AWS_S3_BUCKET_NAME}/ --exact-timestamps --delete

workflows:
  build-and-deploy:
    jobs:
      - build-and-test
      - deploy:
          requires:
            - build-and-test
          filters:
            branches:
              only: main
```

#### jobs, workflowsに加えて、新しく`executors`が追加
executorsはCircleCIで実行するテストやデプロイを動かすための環境（jobで使用する）になります。Step. 1ではテストとビルドのみ実行で、Node.js環境だけ必要でしたのでjobsの中に書いてましたが、デプロイではaws-cliが必要です。jobs内で異なる環境を使う為、executorsにそれぞれ定義して、ジョブの冒頭でそれぞれを指定して使うという仕組みです。

#### jobsの中に deploy ジョブを追加
- 必要なパッケージのインストール
- CircleCIに登録したアクセスキー、シークレットアクセスキーを指定
- AWS S3にビルドしたファイルをアップロード

と言う流れです。

#### workflowsでの分岐
プルリクエスト作成時はテストとビルドだけ実行し、デプロイはmainブランチにマージされた時だけ実行させるようにfilterで分岐してあります。
試しにブランチを切って、プルリクエストを作ってみてください。`build-and-test`だけが実行されます。そのプルリクエストをマージすると`deploy`が実行されます。

#### 環境変数の参照
`${AWS_ACCESS_KEY}` は(2)で設定した環境変数を参照しています。


この修正をレポジトリにプッシュします。（ブランチ切っていたらプルリクをmainにマージしておいてください）

### Step 4.
Step 3.が無事にできれば、CI/CDで自動デプロイまで実行されて成功すると思います。

![スクリーンショット 2025-06-17 23.01.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/d105d359-5988-4726-8d3f-5f166c553eea.png)

AWS S3の該当のバケットのページを開くと以下の様に、`index.html`などのオブジェクトが追加されていれば、デプロイされています。

![スクリーンショット 2025-06-17 23.02.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/9f1bb635-e5c6-48d7-803b-ce59ade11012.png)

ただデプロイはできてますが、まだページが閲覧可能な状態ではありません。

アクセス許可タブを開いて、バケットポリシーの`編集`ボタンをクリックします。

![スクリーンショット 2025-06-17 23.04.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/63568c2f-b18a-4590-808f-3ca67463c98f.png)


以下のJSONファイルの`自分のバケット名`の箇所をご自分のバケット名に変更して貼り付けて保存してください

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::自分のバケット名/*"
        }
    ]
}
```

保存できたら、`index.html`をクリックして、オブジェクトの概要のオブジェクトURLを開いて見てください。ページが開けば成功です。

これで`GitHub`と`CircleCI`と`AWS S3`が繋がって**CI/CDパイプライン**が構築できました。


## まとめ
本記事ではGitHub、CircleCIを使ってミニマムなCI/CDパイプラインを構築しました。
GitHubActions、Bitbucket Pipelineでもやり方は大きく変わらないと思います。