(CI/CD)Deploy React app to AWS s3 using Github actions

Sharath Vignesh
6 min readMay 27, 2021

I recently wanted to setup a CI/CD pipeline to automatically deploy my react app to my s3 bucket. This article assumes you know git, react basics and have an AWS account with appropriate rights. So let’s get started.

AWS s3 setup

Let us first setup our s3 bucket in AWS.

  1. Log in to your AWS console and select s3 from services.
  2. Create a new bucket with the following settings,
  • Provide name and region.
  • Unselect Block all public access. (in case you want your website to be publicly accessible)
  • Select I acknowledge current settings as shown below.
unselect Block public access and acknowledge.
  • You can leave other settings as it is for now and scroll all the way down to click create bucket button.

3. Now that your bucket is created, let us modify the bucket policy like below (under Permissions tab),

  • You’re basically allowing(effect) the public(principal) to view(action) your whole app(resource).
{
"Version": "2021-05-27",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<your-bucket-name>/*"
}
]
}

4. Now, enable static website hosting (under Properties tab)

  • Select use this bucket to host a website option.
  • You can fill index.html for index and error document. (important)

5. Now that you’ve setup your s3 bucket, you can manually upload your build folder or a hello word html file and try opening it to check if your website works fine.

Github actions setup

Having setup our s3 bucket, lets get started with setting up our Github.

  1. Once you upload your code to Github, under Actions tab, click setup workflow yourself.

2. It will create a .yml file for you like below,

  • Let’s first click start commit and make it a part of our repo.

3. Now, let’s pull master and start editing the main.yml file

<your_repo> ----> .github ----> workflows ----> main.yml

4. The .yml file has three parts as below,

  • To put it in simple words, we are saying “Please run certain commands for me whenever my master branch changes”
# This is a basic workflow to help you get started with Actionsname: CI# Controls when the action will run.on:# A workflow run is made up of one or more jobs that can run sequentially or in paralleljobs:
  • You can leave the name as CI(as above).

* On Part

  • Let us add our main branch name like below.

Translating below snippet to english, we are specifying our branch name and on what actions(pull, push) it must build and deploy for us.

on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
branches: [ master ]
pull_request:
branches: [ master ]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

* Jobs part

  • Here you can specify all the steps you want to perform before deploying to s3, like (build, test etc). Here we will just see about build.

To put it in simple words, we are saying, hey “Build my app on ubuntu-latest with node-version 12.18.3

jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [12.18.3]
  • After strategy, we are going to add instructions as below snippet.

Crux of the below snippet is that, we are writing steps to checkout out master branch and do an npm i and then build our app using npm build

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Use the node version specified in the strategy
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}

# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: Git checkout
uses: actions/checkout@v2

# Install packages
- name: Install packages
run: |
npm install

# Build an optimized production build
- name: Production build
run: |
unset CI
npm run build

# Deploy to the S3 server
  • Last step is to give instructions to deploy to s3.
    Thanks to jakejarvis’s action, we can easily deploy to our s3 bucket. Translating the below snippet to english,

We are asking Jake’s action to deploy build folder to our s3 bucket.

# Deploy to the S3 server
- name: Deploy to S3
uses: jakejarvis/s3-sync-action@master
with:
args: --acl public-read --delete
env:
AWS_S3_BUCKET: ${{ secrets.AWS_PRODUCTION_BUCKET_NAME }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
AWS_REGION: ${{ secrets.AWS_REGION }}
SOURCE_DIR: "build"

After adding all the above, your .yml file should look like this,

# This is a basic workflow to help you get started with Actions

name: CI

# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the master branch
push:
branches: [ master ]
pull_request:
branches: [ master ]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [12.18.3]

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Use the node version specified in the strategy
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}

# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: Git checkout
uses: actions/checkout@v2

# Install packages
- name: Install packages
run: |
npm install

# Build an optimized production build
- name: Production build
run: |
unset CI
npm run build

# Deploy to the S3 server
- name: Deploy to S3
uses: jakejarvis/s3-sync-action@master
with:
args: --acl public-read --delete
env:
AWS_S3_BUCKET: ${{ secrets.AWS_PRODUCTION_BUCKET_NAME }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
AWS_REGION: ${{ secrets.AWS_REGION }}
SOURCE_DIR: "build"

5. One last step pending is to add our AWS_PRODUCTION_BUCKET_NAME, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_REGION to our Github secrets (under settings tab).

  • AWS_PRODUCTION_BUCKET_NAME : name of your bucket.
  • AWS_REGION : your bucket’s region
  • For AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY ,
    Please go to My security credentials
  • Click create access key, copy access key id and secret access key id , copy them to Github secrets.

6. You are finally done with the setup. Go ahead, push some changes to master, you’ll see build happening.

7. Once it is success, you can visit your app hosted on s3 to verify the changes.

That’s it :) You’re CICD is ready.

Note: You can add staging or dev env too. Just create appropriate file under workflows, select the branch for staging and config your bucket names and you should be good.

Thank you for reading !

--

--

Sharath Vignesh

Peace-monger . Optimist . Wanderlust . Software Engineer . Foodie