9. CICD Pipeline - GitHub to S3

GitHub logo and AWS CodePipeline Logos

How to use AWS infrastructure to create a git push CI/CD pipeline for a Nuxt 3 Static website built and deployed to a AWS S3 bucket with a fully qualified custom domain name

Why Use a CICD pipeline?

A Continuous Integration and Continuous Delivery (CICD) system creates stress-free and consistent deployments. When you are dealing with multiple sites, this consistency is key. The pros and cons of using CICD vs. manual are:

Why Use a CICD Pipeline?

The main reasons to use a CICD pipeline are:

  1. Automation
    • once set up, the pipeline will automatically build, stage, and deploy your site whenever changes are pushed to the designated branch in your GitHub repository.
  2. Consistency
    • the pipeline ensures that the build and deployment process is consistent and repeatable, reducing the risk of human error.
    • The Build Machine is fresh, dedicated to building and always on-demand. No chance of update, installation issues, or viruses.
    • Your source code is pulled fresh from GitHub. No chance of uploading code that hasn't been checked-in.
    • Your NPM dependencies are always installed anew on each build.
  3. Speed
    • the pipeline can significantly speed up the build and deployment process, allowing for faster updates and releases.
    • Downtime for your web-site is minimal
    • You can roll-back to previous versions
  4. Version Control Integration
    • the pipeline can be integrated with version control systems like GitHub, allowing for automatic builds and deployments triggered by code changes.
  5. Testing and Validation
    • the pipeline can include automated testing and validation steps, ensuring that code changes do not introduce bugs or issues.
    • the CICD can put insert 'approval' checkpoints or stages
  6. Solo Scalability
    • the pipeline can easily scale to handle multiple projects deployments. As a solo developer as I scale by creating more than one site, I can use the same pipeline with minor configuration changes.

CICD Cons

  1. It costs money. This can be mitigated by using small build machines and unnecessary build times (the main driver of money is how many minutes of build machine time is used.)
  2. CICD errors require 'operational' skill set to locate and fix. On the other hand, you can have similar problems and skill set issues with the manual method as well, so it is usually easier with a CICD than finding out what manual steps you took.

Cloud Infrastructure

A detailed overview and how-to guide on creating the cloud infrastructure required for hosting a Nuxt static site on AWS S3 can be found in the Nuxt on AWS Infrastructure article.

Hosting Buckets

The project uses three S3 buckets to host the different stages of the site.

EnvironmentS3 Bucket Name <=> SubdomainDescription
Productionyourdomain.com <=> https://yourdomain.comThe live production bucket site, works with CloudFront (hence https)
Stagingstage.yourdomain.com <=> http://stage.yourdomain.comThe staging site. Prod builds are first deployed here and then after approval can deploy from stage to prod
Developmentdev.yourdomain.com <=> http://dev.yourdomain.comThe development site for local testing in the cloud. Purely for dev and quick test purposes.

Configuring package.json

Once you have your AWS S3 buckets for prod, stage, and dev and CloudFront for prod, you should replace the placeholders in package.json to your infrastructure

package.jsonDescription
JMSTprodS3Replace with your project production S3 bucket, this will be the same as your Domain Name, e.g., yourdomain.com
JMSTdevS3Replace with your project staging S3 bucket name, e.g., dev.yourdomain.com
JMSTstageS3Replace with your project staging S3 bucket name, e.g., stage.yourdomain.com
JMSTCloudFrontIdReplace with your CloudFront Distribution ID for your production site. This is used to invalidate the CloudFront cache after deployment to prod

Build and Deploy Scripts

Within package.json file, the scripts section contains a number of useful build and deploy scripts for managing your JAMStart project deployment to AWS S3. These scripts can be run using npm run <script-name> command.

This scripts assume you have a locally configured AWS CLI with sufficient permissions to access and modify the S3 buckets and CloudFront distribution.

Each project script has a format of

  • <action>:<target>:<optional target2>

where:

  • <action> - what you want to do,
    • clean: deletes files at target, single target
    • copy: copies generated files to target, two targets
    • fix: fixes S3 hosted files, single target
    • deploy: copies and fixes generated files for hosting in S3 buckets, two targets
    • diff: compares sitemaps between two targets, two targets
    • precheck: generates files, runs tests and diffs sitemap in preparation for deployment (smoke test), single target
    • invalidate: invalidates CloudFront cache for target, single target
  • <target>, <optional target2> - the target buckets
    • build - target is the current build machine, local or cloud, in the ./output/public directory, the generated files
    • prod - target is related to cloud production environment
    • stage - target is in cloud staging environment
    • dev - target is in cloud development environment

For example, deploy:stage:prod means to deploy from the staging S3 bucket to the production S3 bucket.

Development to Prod Workflow

In a typical deployment to production workflow, you would:

  1. Development and test locally using
    • npm run dev and localhost:3000
  2. Generate the static site locally and compare to production using
    • npm run precheck:prod
  3. Deploy the generated site to the development environment using:
    • npm run deploy:build:dev
  4. Validate the development site at http://dev.yourdomain.com
  5. Rinse and repeat steps 1-4 until satisfied
  6. Git push your changes to the main branch
  7. The CICD pipeline will automatically build and deploy to staging S3 bucket.
    • Note, if you are not using CICD, you can manually deploy to staging using, npm run generate, npm run deploy:build:stage, npm run fix:stage
  8. Validate the prod build at the staging site at http://stage.yourdomain.com
  9. When approved, deploy the staging site code to production (note even though this run manually you are just copying from staging to prod):
    • npm run deploy:stage:prod
  10. Invalidate the CloudFront cache to ensure the new site is live and any cloud cached files are updated using:
    • npm run invalidate:prod

CICD Pipeline

A detailed overview and hot-to guide on creating a CICD pipeline for building and deploying a Nuxt static site to AWS S3 can be found in the Nuxt on AWS CICD Pipeline - GitHub to S3 article.

Production Pipeline

The Prod Pipeline will gather your source from git, build it, and deploy it to production infrastructure.

Source Build Deploy

The base Build and Deploy pipeline consists of three Stages

  1. Source
  2. Build
  3. Deploy

Source

The source stage will trigger on a 'git push' to the main branch (or whatever git branch you designate). It will copy all the code from GitHub to the AWS CodeBuild Machine. The package from GitHub to AWS Codebuild is called SourceArti and is stored in CodePipeline S3.

Build

A CodeBuild EC2 machine is spun up on-demand. It is a Linux:g1.small machine with Free Tier 100 build minutes a month. For my blog I typically use about 1 1/2 minutes - 2 minutes a build.

The SourceArti from Source is copied and uncompressed onto the file-system of the CodeBuild EC2 machine.

The CodeBuild machine then follows the phases in the buildspec.yml file in the root of the repo. It will do the npm install reading the repo package.json file and installing packages directly. It will run the npm run generate command to layout your Nuxt SSG site.

If successful, the built files from dist will be compressed and stored in the CodePipeline S3 bucket as BuildArti.

Deploy

The CodeDeploy phase will grab the BuildArti compressed files, decompress and copy the files to your live website S3 bucket.

New CodePipeline Steps

Important, you should have created a custom domain, prepared an S3 bucket and received a site certificate prior to creating the AWS CodePipeline.

  1. Requisition a S3 bucket configured for Static Site Hosting
  2. Requisition a CodePipeline - Git Sync, AWS CodeBuild, Deploy to S3 Bucket
  3. Requisition a Route 53 hosted zone
    • Non-certificate (http warning)
    • CloudFront with certificate (https)
  4. Requisition a CloudFront Distribution
  5. Requisition a https Certificate

Create an AWS CodePipeline that will perform three tasks.

  1. Attach and watch a GitHub repo branch for changes
  2. Initiate AWS Codebuild and generate your Nuxt 3 Static Site
  3. Copy the built Nuxt Static Site into your S3 site bucket

Create a CodePipeline

From AWS Console find CodePipeline and choose 'Create'

  • Create a unique name for the pipeline
  • Choose Queued
  • Leave the pipeline role

Next

Pipeline Git sync

Choose a stage source

FieldState
Source Provider'GitHub (version 2)'
ConnectionUse existing or choose 'Connect to GitHub'
Repository name(pick your repo)
Default branch(pick your branch)
Output artifact formatCodePipeline default
Trigger - Trigger typeNo filter (launches on push)

Next

Pipeline CodeBuild

Build - Optional

Build provider → AWS codeBuild

FieldState
Build Provider'AWS CodeBuild'
Region(pick your region)
Input artifactsSourceArtifact (from the GitHub sync above)
Project Name(pick your project name or choose 'Create Project')
Environment variables(leave blank or add)
Build typeSingle build

Create Build Projects

FieldState
Project name(pick a project name)
Source 1 - PrimarySource provider - AWS CodePipeline
Environment Provisioning modelOn-demand
Environment imageManaged image
Environment ComputeEC2
Operating systemAmazon Linux
RuntimeStandard
Imageaws/codebuild/amazonlinux2-x86_64-standard:5.0
Image VersionAlways use the latest image for this runtime version
Buildspec Build specificationsUse a Buildspec file
Buildspec name(leave blank or buildspec.yml)

Pipeline buildspec.yml

Add the following buildspec.yml file to your repo. These are the instructions that will be used to build the Nuxt static site as well as indicating where the built files will exist after build.

version: 0.2

phases:
  install:
    commands:
      - npm install
  build:
    commands:
      - npm run generate
artifacts:
  files:
    - '**/*'
  base-directory: 'dist'

AWS CodeDeploy

The third stage of the pipeline, deploy, copies the built files to the S3 website bucket

FieldState
Deploy provider'Amazon S3'
Region(pick your Region, same as build)
Input artifacts'BuildArtifact'
Bucket(pick your bucket)
Extract file before deployenabled

Verify

If all your steps are correct, your code pipeline should run. You should validate by opening the website at the S3 bucket website endpoint.

S3 → {static site bucket} → properties → Static website hosting | Bucket website endpoint

Dev Infrastructure (optional)

The Dev (Development) Infrastructure consists of cloud resources that host your development web site - for testing and validation before deployment. A dev infrastructure and pipeline are optional and may or may not be implemented for your site needs. A content heavy site with multiple proofreaders, editors, or authors, might want a dev infrastructure to view the web site before deployment to production. A solo author might be fine will validating the site locally and skip the dev infrastructure.

Recommended Resources

  1. AWS Route53 Hosted zone
  2. AWS S3 Bucket

Dev Pipeline (optional)

A Developer Pipeline is used to host your site for testing before deploying to prod

Resources

The following videos illustrate many of the steps used here: