/Middleman-AWS-Prismic-CircleCi

Open source starter repo for generating a static site using Middleman, Amazon Web Services (AWS), Prismic, and CircleCi.

Primary LanguageRuby

alt text

Rainfactory's Speedy and Traffic Resilient Static Site Setup

❤️ Middleman ❤️ CircleCi ❤️ AWS ❤️ Prismic

Welcome to our Middleman-AWS-Prismic-CircleCi open source project! We've used this setup to create high-traffic crowdfunding sites for Rainfactory clients (e.g. LunaSleep, Swypcard, Fove ) at Monsoon. The following provides step-by-step instructions for generating a static site with Middleman, Amazon Web Services, CircleCI and Prismic.

Alt text

Ingredients

Table of Contents

  1. Running the local web server
  2. Create a new Middleman Site
  3. Set permissions and download AWS access keys in AWS Identity & Access Management (IAM)
  4. Setup an AWS S3 Bucket
  5. Create an AWS Cloudfront Distribution
  6. Add Middleman s3 sync gem and configuration
  7. Add Middleman cloudfront gem and configuration
  8. Pushing assets to AWS from your console as a first test
  9. CircleCI for continuous deployment
  10. Set Environmental Variables in CircleCi
  11. Prismic Content Management
  12. Launch in AWS

1. Running the local web server

If you're pulling down this repo and want to get it running, do the following:

  1. Bundle Gems

    bundle install

  2. Compile files

    bundle exec middleman build

  3. Start a local web server running at: http://localhost:4567/

    bundle exec middleman

2. Create a new Middleman site

  1. Follow steps to install Middleman and start a new site

3. Set permissions and download AWS access keys in AWS Identity & Access Management (IAM)

Alt text

  1. After an AWS account has been setup, go to AWS IAM to set permission and download AWS keys (AWS access key id and AWS secret key). You can set specific permissions to Users or Groups as needed (read more about it here). Here's a basic example of adding AdministratorAccess to a User (see below). You'll need to save your AWS Keys for setting environmental variables in CircleCi (AWS_ACCESS_KEY_ID, AWS_SECRET_KEY).

Create a New User Alt text

Enter a user name Alt text

Download User Security Credentials Alt text

Attach Policy to the new User Alt text

Check off AdminstratorAccess Alt text

4. Setup an AWS S3 Bucket

Alt text

  1. Create an AWS S3 Bucket

    Go to Services > S3. Create a Bucket in S3, add a meaningful name (e.g. myappname-production) and region option, e.g. Northern California).

    Once you choose a region, search region codes here: http://www.bucketexplorer.com/documentation/amazon-s3--amazon-s3-buckets-and-regions.html

    You will need to add your region property in config.rb in the Setup an AWS S3 Bucket step.

    alt text

  2. Enable Static Website Hosting

    Go to your bucket and click Properties. Under Static Website Hosting, click Enable website hosting and add index.html in Index Document and error.html in Error Document. Save.

    alt text

  3. Add CORS Configuration for loading fonts from an origin other than your web application (e.g. Font Awesome).

    alt text

More on this

5. Create an AWS Cloudfront Distribution

Alt text

  1. Create an AWS Cloudfront Distribution

    Go to Services > Cloudfront

  2. Select "Web" as your delivery method:

Alt text

  1. Create Distribution
  • Add your S3 bucket name to the Origin Domain Name field. If you click on the field, a list will automatically appear. It should also autofill your Origin ID. (e.g. example-bucket)

    Alt text

IMPORTANT: Delete the pre-autofilled Origin Domain Name (e.g. middleman-sandbox-production.s3.amazonaws.com). Replace this with the AWS S3 Bucket Static Website Hosting Endpoint (e.g. middleman-sandbox-production.s3-website-us-west-1.amazonaws.com, see image below). This is necessary so your AWS Cloudfront domain (e.g. http://dXXXXXXXXXXX.cloudfront.net/) mirrors the AWS S3 static website hosting endpoint.

![Alt text](README_Images/aws_s3_endpoint.png)
  • Under Default Cache Behavior Settings, click on the Forward Headers options list. Click Whitelist Headers. Add Origin

  • Under Distribution Settings, add 'index.html' to the Default Root Object.

  • Click Create Distribution

  1. You’ll be brought to a CloudFront Distributions page in which a Cloudfront domain name will be returned to you. (e.g. http://dXXXXXXXXXXX.cloudfront.net/)

Alt text

More on this:

6. Add middleman s3 sync gem and configuration

Use middeman-s3_sync gem to push assets to your AWS s3 bucket.

Add middleman-s3_sync gem in your Gemfile and follow configuration instructions. See config.rb.



    activate :s3_sync do |s3_sync|
      # The name of the S3 bucket you are targeting.
      s3_sync.bucket                     = ENV['AWS_S3_BUCKET_NAME']
      # The AWS region code for your bucket.
      # For region codes: http://www.bucketexplorer.com/documentation/amazon-s3--amazon-s3-buckets-and-regions.html
      s3_sync.region                     = ENV['AWS_REGION']
      s3_sync.aws_access_key_id          = ENV['AWS_ACCESS_KEY_ID']
      s3_sync.aws_secret_access_key      = ENV['AWS_SECRET_KEY']
      #s3_sync.delete                     = true # We delete stray files by default.
      #s3_sync.after_build                = true # We do not chain after the build step by default.
    end

7. Add middleman cloudfront gem and configuration

We're using middleman-cloudfront for AWS CloudFront cache invalidation.

Add middleman-cloudfront gem and follow configuration instructions. See config.rb.


      activate :cloudfront do |cf|
        cf.access_key_id                   = ENV['AWS_ACCESS_KEY_ID']
        cf.secret_access_key               = ENV['AWS_SECRET_KEY']
        cf.distribution_id                 = ENV['PRODUCTION_CLOUDFRONT_DISTRIBUTION_ID']
        cf.filter                          = /\.html$/i
        #cf.after_build                     = false  # default is false
      end

      after_s3_sync do |files_by_status|
        invalidate files_by_status[:updated]
      end

You can watch invalidations processing if you go to CloudFront Distributions > Click on Cloudfront distribution ID > Invalidations tab

Alt text

More on this

8. Pushing assets to AWS from your console as a first test

If you need to experiment with pushing assets to AWS S3 locally, you can do the following:

  • Create an aws.yml file at the root of the application. Note that this file will be ignored for security (See .gitignore)
  • Add environmental variables from AWS LastPass notes to aws.yml
  • Uncomment ENV = YAML::load(File.open('aws.yml')) in config.rb

      AWS_S3_BUCKET_NAME: ''
      AWS_REGION: ''
      AWS_ACCESS_KEY_ID: ''
      AWS_SECRET_KEY: ''
      PRODUCTION_CLOUD_DISTRIBUTION_ID: ''
    
  • Run these commands to push assets to AWS middleman-sandbox-staging bucket

bundle exec middleman build --verbose

bundle exec middleman s3_sync --bucket=your-aws-bucket-name

PRODUCTION_CLOUDFRONT_DISTRIBUTION_ID=XXXXXXXXXXX bundle exec middleman invalidate

9. CircleCi for continuous deployment

CircleCi will run deployment commands only when changes are merged into the branch specified in circle.yml (e.g. branch:master ).

  1. Setup your Github account with CircleCi.
  2. In CircleCi, add your project/repo.
  3. Create a circle.yml file in the root of your repo.

  dependencies:
    pre:
      - bundle install

# To speed things up, you can also cache directories.  Uncomment this and replace this with code above.
# dependencies:
#   cache_directories:
#     - "~/.rvm/gems/ruby-2.0.0-p451"

  deployment:
    production:
      branch: master
      commands:
        - bundle exec middleman build --verbose

        # Push Middleman build to your AWS S3 bucket
        - bundle exec middleman s3_sync --bucket=middleman-sandbox-production

        # Invalidate cache in AWS Cloudfront
        - PRODUCTION_CLOUDFRONT_DISTRIBUTION_ID= bundle exec middleman invalidate

10. Set environmental variables in CircleCi

Add the following AWS environmental variables required for continuous deployment in CircleCi. In this case, we're pushing assets to your AWS S3 bucket and invalidating the cache in AWS Cloudfront.

Alt text

11. Prismic content management and adding a webhook with CircleCi

  1. Follow instructions in Prismic to create a Document Mask and create content.

  2. See Prismic's documentation for adding the API object and querying your Prismic repository with Ruby. See config.rb as an example.


    api = Prismic.api('https://prismic-project-example.prismic.io/api')
    response = api
     .form('everything')
     .query('[[:d = at(document.type, "document-mask-name")]]')
     .submit(api.master_ref)

    @something_to_put_in_view = response.results
  1. Go to your prismic account. Settings > Webhooks.

  2. Go to CircleCi and create an API token (Settings > API Tokens). Create a token for the prismic webhook.

Alt text

  1. Add a POST request url (see below) from CircleCI in the Webhooks section of Prismic to trigger a build in CircleCi. This will trigger a build targeted at the specified Github branch every time a document is published.

https://circleci.com/api/v1/project/:username/:project/tree/:branch?circle-token=:token

Alt text

More documentation resources on creating a webhook:

12. Launch in AWS

You may use your own DNS provider (e.g. GoDaddy) vs Amazon's Route 53 DNS manager. Add your official domain name to Alternate Domain Names (CNAMEs) under the General tab in Cloudfront Distributions. Make sure you place the urls on seperate lines.

Alt text

More on this

The MIT License (MIT)

Copyright (c)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.