Baking Rack

Tests

Bake and Push static webpages generated by a Rack application.

Baking Rack Logo

Table of Contents

Why?

Why static webpages?

When paired with a good hosting service, static webpages are cheaper to host, easier to maintain, and harder to hack than dynamic ones because there are less moving parts.

Why one more Ruby static webpage generator?

BakingRack is a narrowly-focused library, not an all-encompassing framework.

Jekyll, Middleman, Nanoc, and Bridgetown are frameworks that define how your site is organized. BakingRack is only interested in your site being a Rack app (protip: all those frameworks support Rack) in order to gather its static pages and publish them to a static webserver or CDN.

Getting Started

Start by including baking_rack in your Gemfile:

gem 'baking_rack'

# when deploying to S3
gem 'aws-sdk-s3'

Then run bundle install.

Configuration

BakingRack.config do |c|
  c.builder = BakingRack::Builder.new(app: YourRackApp)
  c.deployer = BakingRack::AwsS3::Deployer.new

  c.define_static_routes do
    # Render this explicit path using your Rack app.
    get "/about.html"

    # Collect routes from static data
    BlogPost.find_each do |post|
      get post.path
    end

    # Expect statuses other than 200
    get "/404.html", status: 404
  end
end
# view available commands
$ bundle exec baking_rack help

# view a specific command's options
$ bundle exec baking_rack help [COMMAND]

# perform the build and deploy
$ bundle exec baking_rack publish

Dry Run

Deployers support the dry_run keyword argument which instructs it to output what will be deployed without actually performing it.

Ruby on Rails

BakingRack provides some additional help for building static webpages from Ruby on Rails applications, just use its custom builder class:

# config/initializers/baking_rack.rb

BakingRack.config do |c|
  c.builder = BakingRack::Rails::Build.new

  b.static_routes do
    # path helpers are exposed here
    get root_path
  end
end

Intelligent defaults:

  • app defaults to Rails.application
  • domain_name defaults to Rails.application.config.hosts.first

AWS + Terraform + GitHub

  1. Define the terraform module and some outputs
module "baking_rack" {
  source = "git@github.com:dcunning/baking_rack.git//terraform/aws?ref=v<%= BakingRack::VERSION %>"

  bucket_name       = "${module.label.id}-www"
  domain_name       = var.domain_name
  github_repository = var.github_repository
  branch_name       = var.github_branch_name
}

output "baking_rack" {
  value = module.baking_rack
}
  1. Add an origin to your CloudFront distribution
  origin {
    domain_name = module.baking_rack_bucket.website_endpoint
    origin_id   = "S3-${module.baking_rack.bucket_name}"

    custom_origin_config {
      http_port              = 80
      https_port             = 443
      origin_protocol_policy = "http-only"
      origin_ssl_protocols   = ["TLSv1", "TLSv1.1", "TLSv1.2"]
    }

    # Prevents S3 objects from being accessed outside CloudFront
    custom_header {
      name  = "User-Agent"
      value = module.baking_rack.handshake
    }
  }
  1. Apply the terraform plan
terraform init
terraform apply
  1. Generate a GitHub workflow

Use GitHub Actions to automatically publish the latest main to the S3 bucket. The IAM role and bucket name are outputs from the terraform apply command.

name: Publish to production

on:
  push:
    branches: [ main ]

env:
  AWS_REGION : "us-east-1"

jobs:
  Publish:
    runs-on: ubuntu-latest

    permissions:
      id-token: write
      contents: read

    steps:
      - name: Git clone the repository
        uses: actions/checkout@v3

      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true

      # https://aws.amazon.com/blogs/security/use-iam-roles-to-connect-github-actions-to-actions-in-aws/
      - name: configure aws credentials
        uses: aws-actions/configure-aws-credentials@v1.7.0
        with:
          role-to-assume: "(FROM TERRAFORM OUTPUT)"
          role-session-name: GitHub_to_AWS_via_FederatedOIDC
          aws-region: ${{ env.AWS_REGION }}

      - name: Publish Site
        env:
          RAILS_ENV: production
          RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
          BUCKET_NAME: "(FROM TERRAFORM OUTPUT)"
        run: |
          bin/rails assets:precompile
          bundle exec baking_rack publish

Contributing

If you have problems, please create a GitHub Issue.

Versioning

baking_rack follows Semantic Versioning 2.0 as defined at https://semver.org.

License

This code is free to use under the terms of the MIT license.