Deploy React SPA with CodePipeline and CodeBuild using AWS CDK

January 23rd, 2020 • 748 Words

There are plenty of tools and services for continuous delivery available. Most of them are either directly built into the source code management tools you already use, or perfectly integrate with them. You might be familiar with CircleCI, Travis CI, GitLab CI, or GitHub Actions.

Of course, AWS has its services for covering the needs of continuous integration and continuous delivery. With AWS CodePipeline you can manage and orchestrate the stages and tasks of a pipeline and AWS CodeBuild is a slick pay-per-use approach for executing commands in containerized environments.

Final Goal

With the example project on GitHub, you can create a CI/CD pipeline using AWS CodePipeline and deploy a React SPA written in TypeScript to an Amazon S3 bucket to have a static website.

The provided GitHub repository contains a basic application created with create-react-app and the pipeline consists of thee steps: checkout, build, and deploy.

Every push to the master branch of the repository will trigger a pipeline run and deployment. Of course, everything is configured with the AWS CDK.

AWS CodePipeline

Introduction

For most services, you configure a pipeline with a YAML configuration in your source code’s repository. A pipeline typically consists of stages and jobs inside those stages. Most of the time, you will end up with stages for code checkout or dependency installation, and of course for testing, building, and finally deploying your application.

Using the AWS CDK to describe the AWS CodePipeline and AWS CodeBuild resources, you can get rid of the language switch and implement the pipeline’s components in the same language as you write your application: TypeScript, for example.

Preliminaries

You need to have the AWS CDK installed on your computer, configured AWS environment variables for CLI usage, cloned the repository, and - of course - install all needed dependencies.

# Configure AWS CLI
$ > export AWS_ACCESS_KEY_ID="…"
$ > export AWS_SECRET_ACCESS_KEY="…"
$ > export AWS_SESSION_TOKEN="…"

# Install AWS CDK
$ > yarn global add aws-cdk

# Clone GitHub repository
$ > git clone https://github.com/sbstjn/cra-pipeline.git
$ > cd cra-pipeline

# Install dependencies
$ > yarn install

GitHub Token

To access your source code located at GitHub, you need to configure a personal access token and store it somewhere in AWS so AWS CodePipeline can access it. The AWS Secrets Manager is a perfect fit for this job.

Besides the permission to access your (private) repositories, the token needs to have permission to configure Webhooks in your GitHub repository. After you created the access token, create a new secret in AWS Secrets Manager with the token as the value.

$ > aws secretsmanager create-secret \
    --name GitHubToken \
    --secret-string abcdefg1234abcdefg56789abcdefg \
    --region us-east-1

{
  "ARN": "arn:aws:secretsmanager:us-east-1:123456789001:secret:GitHubToken-uNBxTr",
  "Name": "GitHubToken",
  "VersionId": "4acda3d1-877f-4032-b38e-17bc50239883"
}

Configuration

The project includes a config.ts file to configure the location of your source code and the AWS region in which you want to deploy your AWS CodePipeline and AWS CodeBuild resources.

export const config = {
  github: {
    owner: 'sbstjn',
    repository: 'cra-pipeline'
  },
  env: { region: 'us-east-1' }
}

If you never used the AWS CDK in your AWS account or desired region, you need to bootstrap the AWS CDK basics.

$ > yarn cdk bootstrap --region us-east-1

⏳  Bootstrapping environment aws://123456789001/us-east-1...

0/2 | 5:06:49 PM | CREATE_IN_PROGRESS   | AWS::S3::Bucket | StagingBucket
0/2 | 5:06:50 PM | CREATE_IN_PROGRESS   | AWS::S3::Bucket | StagingBucket Resource creation Initiated
1/2 | 5:07:11 PM | CREATE_COMPLETE      | AWS::S3::Bucket | StagingBucket

✅  Environment aws://123456789001/us-east-1 bootstrapped.

Deploy AWS CodePipeline and AWS CodeBuild

Thanks to the AWS CDK, the deployment of the infrastructure is nothing more than using an additional command:

$ > yarn cdk deploy Pipeline

Pipeline: deploying...
Pipeline: creating CloudFormation changeset...

✅  Pipeline

Outputs:
Pipeline.WebsiteURL = http://pipeline-files8e6940b8-3p9gac9qjzax.s3-website-us-east-1.amazonaws.com

The WebsiteURL will point to an empty Amazon S3 bucket. When you push a new commit to the master branch of your repository, AWS CodePipeline is triggered using GitHub Webhooks and AWS CodeBuild will build your React application. If the checkout of your sources and the React build process finished without errors, AWS CodePipeline will copy all static files to your S3 bucket. Done.

The Details

I try to store all AWS CDK files in a infra folder; You’ll find a folder for the CloudFormation Stacks, a buildspec.yml file for AWS CodeBuild and a index.ts for AWS CDK. All the interesting parts are stored in infra/stacks/pipeline.ts and I’ll try to briefly walk you through the contents.

First things first: You need an S3 bucket to store the static files in the end.

// Amazon S3 bucket to store CRA website
const bucketWebsite = new S3.Bucket(this, 'Files', {
  websiteIndexDocument: 'index.html',
  websiteErrorDocument: 'error.html',
  publicReadAccess: true
})

To pass artifacts from one AWS CodeBuild task to another, you need artifacts:

// AWS CodeBuild artifacts
const outputSources = new CodePipeline.Artifact()
const outputWebsite = new CodePipeline.Artifact()

Now, of course, you need a pipeline in AWS CodePipeline!

// AWS CodePipeline pipeline
const pipeline = new CodePipeline.Pipeline(this, 'Pipeline', {
  pipelineName: 'Website',
  restartExecutionOnUpdate: true
})

As mentioned in the introduction, you will end up with three stages and actions in your pipeline. Let’s start with the basic stage for downloading your sources from GitHub. This stage will read your GitHub access token from AWS Secrets Manager.

// AWS CodePipeline stage to clone sources from GitHub repository
pipeline.addStage({
  stageName: 'Source',
  actions: [
    new CodePipelineAction.GitHubSourceAction({
      actionName: 'Checkout',
      owner: props.github.owner,
      repo: props.github.repository,
      oauthToken: CDK.SecretValue.secretsManager('GitHubToken'),
      output: outputSources,
      trigger: CodePipelineAction.GitHubTrigger.WEBHOOK
    })
  ]
})

All artifacts of the initial action are stored in outputSources and will be used as the input for the next action to build your static files. The build process uses AWS CodeBuild, configured with a simple buildspec.yml file.

// AWS CodePipeline stage to build CRA website and CDK resources
pipeline.addStage({
  stageName: 'Build',
  actions: [
    // AWS CodePipeline action to run CodeBuild project
    new CodePipelineAction.CodeBuildAction({
      actionName: 'Website',
      project: new CodeBuild.PipelineProject(this, 'BuildWebsite', {
        projectName: 'Website',
        buildSpec: CodeBuild.BuildSpec.fromSourceFilename('./infra/buildspec.yml')
      }),
      input: outputSources,
      outputs: [outputWebsite]
    })
  ]
})

Again, the artifacts of this action need to be stored for further processing. This time, it’s the outputWebsite artifact and all you need to do now is to copy all files to your S3 bucket. AWS CodePipeline offers a great default action to handle this task!

// AWS CodePipeline stage to deployt CRA website and CDK resources
pipeline.addStage({
  stageName: 'Deploy',
  actions: [
    // AWS CodePipeline action to deploy CRA website to S3
    new CodePipelineAction.S3DeployAction({
      actionName: 'Website',
      input: outputWebsite,
      bucket: bucketWebsite
    })
  ]
})

That’s all. Nothing fancy, just a straight forward solution using native AWS features and components. Have a good day, thank you for reading, and happy continuous integration!


View on GitHubSource code is published using the MIT License.
  • React SPA with server-side rendering on AWS Lambda

    February 5th, 2020 • 1152 Words • EN

    React is great for creating websites and writing applications using JavaScript. But, whenever the use of React is rejected, one of the most common reasons is the lack of a simple implementation for server-side rendering (SSR). When you search on Google for this topic, you’ll find various approaches…

  • AWS re:Invent 2019 Recap & Videos

    December 15th, 2019 • 360 Words • EN

    I was able to attend the AWS re:Invent 2019 conference. A week full of learning about current and new technologies, services, and general approaches is definitely overwhelming. There is no much content available, during the conference, and as videos and slide decks afterwards. I tried to list my…

  • CloudFormation Best-Practices

    May 1st, 2019 • 457 Words • EN

    You can find plenty of frameworks and tools to provision your AWS resources. Some of them do a great job for a specific purpose, others are more generic. Nevertheless, I do prefer to use native CloudFormation templates as much as possible. Prefix all the things Split up your CFN Stacks Nested…

  • Makefile Best-Practices

    April 30th, 2019 • 167 Words • EN

    The more projects you work on, the more streamlined your tooling gets. Hopefully. Various services using different languages have different tooling requirements, of course. A sweet Makefile can be the entry to a unified tooling interface. Hide Commands Wildcard Targets Foreach and Lists As long as I…

  • AppSync GraphQL API with Custom Domain and CloudFormation

    April 7th, 2019 • 165 Words • EN

    With AWS AppSync, it’s easy to run your own serverless GraphQL service API. Thanks to Velocity Mapping Templates, DynamoDB, and AWS Lambda your can aim for an architecture without any maintenance at all. Getting started with AppSync is not that problem; there are tons of guides and frameworks. AWS…

  • Last Update: 2020-02-17T10:07:11.183Z
    Stresemannstraße 132, 22769 Hamburg
    +49 151 54 64 90 55
    inbox@sbstjn.com