• Stars
    star
    749
  • Rank 60,575 (Top 2 %)
  • Language
    Go
  • License
    MIT License
  • Created almost 10 years ago
  • Updated over 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A reliable static website deploy tool

Stout

View the site →

Stout is a deploy tool for static websites. It takes a website and uploads it to S3 in a more reliable way than other tools. It can be an alternative to paid services like Divshot, to dynamic web servers like Rails, or to manually uploading your site to S3 or an FTP server.

Why You Need Stout

Traditionally uploading your files to S3 introduces a serious caching issue we ran into in practice at Eager. The cache for the various files your site depends on can expire at different times, meaning your users get an inconsistent (broken) set of files for a time after every single deploy. Further, traditional static site deployments don't offer any method of rolling back a previous deploy.

We built Stout to fix these issues.

Features

  • Versions script and style files to ensure your pages don't use an inconsistent set of files during or after a deploy
  • Supports rollback to any previous version
  • Does not depend on any specific build tool or workflow (it is a standalone executable written in Go)
  • Does not require a datastore of any kind to maintain state or history
  • Can be used by multiple developers simultaneously without locking or a danger of inconsistent state
  • Properly handles caching headers
  • Supports deploying multiple projects to various subdirectories of the same site without conflicts
  • Compresses files for faster delivery

Limitations

  • Stout doesn't currently support rolling back files that aren't HTML, JS or CSS (images, videos, etc.). See the Versioning section for more information.
  • All-or-nothing consistency is only guarenteed on a per-html-file basis, not for the entire deploy. See the Consistency section for more information.

Getting Started

Download the stout executable for your system from our latest release into a directory on your $PATH, like /usr/local/bin.

You can use the create command to create a new site. It automatically creates an S3 bucket, a CloudFront distribution, and a user account for deployment. It therefore requires credentials for an Amazon AWS account which has permissions to those services along with Route 53.

stout create --bucket my.website.com --key MY_AWS_KEY --secret MY_AWS_SECRET

You can then deploy your project:

stout deploy --bucket my.website.com --key MY_AWS_KEY --secret MY_AWS_SECRET

If your built files are in another directory, add the --root option:

stout deploy --bucket my.website.com --key MY_AWS_KEY --secret MY_AWS_SECRET --root ./build

If your bucket located not in the default region, which is us-east-1, add the --region option:

stout deploy --bucket my.website.com --key MY_AWS_KEY --secret MY_AWS_SECRET --region us-west-1

If you don't want to deploy all the files in your folder, use the files argument.

stout deploy --bucket my.website.com --key MY_AWS_KEY --secret MY_AWS_SECRET --root ./build --files "*.html,images/*"

Javascript and CSS included in your HTML files will always be included automatically.

The deploy command will give you a deploy id you can use in the future to rollback if you have to:

stout rollback --bucket my.website.com --key MY_AWS_KEY --secret MY_AWS_SECRET a3b8ff290c33

Eventually you'll probably want to move your config to a deploy.yaml file, rather than specifying it in the command every time.

Using the info below you can learn about what the deploy/rollback tools actually do, deploying to subfolders, deploying from your build tool, and rolling back.

Backstory

We wrote Stout because we couldn't find an open-source way to reliably and efficiently deploy our static sites (including our app and blog). We used a traditional upload-to-s3 tool, but caching meant a user could get the new html and styles, but the old scripts, for example, causing sporatic and random errors. It also didn't support reliably rolling back when necessary. We built Stout to be the reliable, production-ready choice for static deploys.

Function

Stout is an executable file built from Go code. The deploy command deploys one or more html files and their dependencies to a specified location in S3. The rollback command takes a deploy id and rolls the project back to that version.

Deploy

The deploy process works by parsing the script and style tags out of one or more html files. It then hashes those files, uploads them prefixed with their hashes, and updates the location of the original script and link tags with the hashed locations.

It generates a deploy id by hashing all of the files in the deploy, and uploads the html files to a location prefixed by the deploy id.

When the uploads are successful, the prefixed html files are atomically copied to their unprefixed paths, completing the deploy.

Rollback

A rollback simply copies the html files prefixed with the specified deploy id to the unprefixed paths.

Deploy Configuration

You can configure the deploy tool with any combination of command line flags or arguments provided in a configuration yaml file.

The options are:

bucket

The S3 bucket to deploy to. In most configurations this bucket should be the origin for the CDN which actually serves your site. It usually makes sense to make this the url you are going to host your site from (i.e. "example.com")

config ("./deploy.yaml")

The location of a yaml file to read any otherwise unspecified configuration from.

dest ("./")

The destination directory to write files to in the S3 bucket. For example if you wanted your this project to end up hosted at yoursite.com/blog, you would specify --dest blog.

root ("./")

The local directory where the files to be uploaded lives. It's common to make this your "./build" directory or the like.

files ("*")

Comma-seperated glob patterns of the files to be deployed (within the --root). HTML files will be parsed, and the CSS/JS they point to will be included (versioned) automatically. If you also include those files in your glob pattern they will be uploaded twice, once with a versioning hash in the URL, again without.

Be sure to include any additional files you would like deployed like images, videos, font files, etc.

You can use relative paths which break out of the root. If you prefix the path with -/, it will be interpreted as relative to the project directory, not the root.

env

The config file can contain configurations for multiple environments (production, staging, etc.). This specifies which is used. See the "YAML Config" section for more information.

key

The AWS key to use. The create command will create an IAM user for each project with access only to the relevant bucket. See the Permissions section for more information.

secret

The AWS secret of the provided key.

region ("us-east-1")

The AWS region the S3 bucket is located in.

If you are getting a The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint. error, specify your bucket --region.

YAML Config

You can provide a yaml file which specifies configuration defaults for the project being deployed. We include this file in each project which will be deployed. This file can have multiple configurations for different environments, along with a default section.

For example, the deploy.yaml for one of our projects looks like:

default:
  root: 'build/'

production:
  key: 'XXX'
  secret: 'XXX'
  bucket: 'eager.io'

development:
  key: 'XXX'
  secret: 'XXX'
  bucket: 'next.eager.io'

Replacing the "XXX"s with our actual credentials.

To deploy to development we run (from the directory with the deploy.yaml file in it):

deploy --env development

A rollback of development would be:

rollback --env development $DEPLOY_ID

Where the deploy id is taken from the output of the deploy you wish to rollback to.

Our public projects use a similar config, but they specify the Amazon credentials as environment vars from the build system, passed in as flags:

deploy --env development --key $AMAZON_KEY_DEV --secret $AMAZON_SECRET_DEV

Never commit Amazon credentials to a file in a public repo. Keep them on your local machine, or in your build system's configuration.

Clean URLS

It's not specific to Stout, but it's worth mentioning that we recommend you structure your built folder to use a folder with an index.html file for each page.

For example, if you want a root and a page at /blog, you would have:

index.html
blog/
  index.html

That way, assuming S3 and CloudFront are configured properly, you'll be able to use the clean URLs / and /blog/.

SSL

Cloudfront has the ability to serve your site using SSL. The general procedure for setting it up is:

  1. Get an SSL certificate for your domain
  2. Upload it to Amazon
  3. Select that certificate in the configuration for the CloudFront distribution Stout creates for you

You will absolutely need more detailed instructions, which you can find here.

Selecting a certificate for you is one of the few things the create command does not do, as it's not always possible to decide which certificate is appropriate. If you need SSL support, you will have to remember to select the cert in the Amazon Console or CLI after running the create command.

Permissions

The AWS user which is used for Stout should have the GetObject, PutObject, DeleteObject, and ListBucket permissions. The create command will set this up for you if you use it.

This is an example policy config which works:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:DeleteObject",
        "s3:ListBucket",
        "s3:PutObject",
        "s3:PutObjectAcl",
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::BUCKET", "arn:aws:s3:::BUCKET/*"
      ]
    }
  ]
}

Be sure to replace BUCKET with your bucket's actual name.

Deploying with CircleCI

Deploying with CircleCI is simply a matter of installing the deploy tool and running it as you would locally. Here's an excerpt of a working circle.yml:

dependencies:
 post:
   - go get github.com/tools/godep
   - git clone [email protected]:EagerIO/Stout.git
   - cd Stout; godep go build -o ../stout src/*.go
   
deployment:
 development:
   branch: dev
   commands:
     - ./stout deploy --env development --key $AMAZON_KEY_DEV --secret $AMAZON_SECRET_DEV

 production:
   branch: master
   commands:
     - ./stout deploy --env production --key $AMAZON_KEY_PROD --secret $AMAZON_SECRET_PROD

If you use environment vars for your credentials, make sure to add them to your Circle config.

If your repo is private, you can specify your Amazon key and secret in your deploy.yaml file, removing the need to specify them in the commands.

Caching

All versioned files (include a hash of their contents in the path) are configured to cache for one year. All unversioned files are configured to cache for 60 seconds. This means it will take up to 60 seconds for users to see changes made to your site.

Versioning

Only JS and CSS files which are pointed to in HTML files are hashed, as we need to be able to update the HTML to point to our new, versioned, files.

Any other file included in your --files argument will be uploaded, but not versioned, meaning a rollback will not effect these files. This is something we'd like to improve.

Consistency

As the final step of the deploy is atomic, multiple actors can trigger deploys simultaneously without any danger of inconsistent state. Whichever process triggers the final 'copy' step for a given file will win, with it's specified dependencies guarenteed to be used in their entirity. Note that this consistency is only guarenteed on a per-html-file level, you may end up with some html files from one deployer, and others from another, but all files will point to their correct dependencies.

Deploying Multiple Projects To One Site

You can deploy multiple projects to the same domain simply by specifying the appropriate dest for each one. For example your homepage might have the dest ./, and your blog ./blog. Your homepage will be hosted at your-site.com, your blog your-site.com/blog.

Using Client-side Routers

It is possible to use a client-side router (where you have multiple request URLs point to the same HTML file) by configuring your CloudFront distribution to serve your index.html file in response to 403s and 404s.

CF

Installing

  • Download the release for your system type from our releases
  • Copy or symlink the stout binary contained in the archive into your path (for example, into /usr/local/bin)

Building

  • Install go and godep
  • Run godep restore ./...
  • Run go build -o ../stout src/*

For a Release (Cross Compiling)

  • Run go get github.com/laher/goxc
  • Run go get code.google.com/p/go.tools/cmd/vet
  • Run ./utils/xc.sh

The first run will take significantly longer than future runs. The built files will be placed in the ./builds directory.

Running

To run the commands for development purposes, run: go run src/*, followed by any command line args you would normally give to the command.

Contributing

Please do, we would love for this to become a project of the community. Feel free to open an issue, submit a PR or contribute to the wiki.

More Repositories

1

pingora

A library for building fast, reliable and evolvable network services.
Rust
20,561
star
2

quiche

🥧 Savoury implementation of the QUIC transport protocol and HTTP/3
Rust
9,191
star
3

cfssl

CFSSL: Cloudflare's PKI and TLS toolkit
Go
8,049
star
4

workerd

The JavaScript / Wasm runtime that powers Cloudflare Workers
C++
6,175
star
5

boringtun

Userspace WireGuard® Implementation in Rust
Rust
6,001
star
6

cloudflared

Cloudflare Tunnel client (formerly Argo Tunnel)
Go
5,870
star
7

flan

A pretty sweet vulnerability scanner
Python
3,910
star
8

miniflare

🔥 Fully-local simulator for Cloudflare Workers. For the latest version, see https://github.com/cloudflare/workers-sdk/tree/main/packages/miniflare.
TypeScript
3,719
star
9

wrangler-legacy

🤠 Home to Wrangler v1 (deprecated)
Rust
3,233
star
10

cloudflare-docs

Cloudflare’s documentation
MDX
3,009
star
11

tableflip

Graceful process restarts in Go
Go
2,549
star
12

workers-rs

Write Cloudflare Workers in 100% Rust via WebAssembly
Rust
2,478
star
13

workers-sdk

⛅️ Home to Wrangler, the CLI for Cloudflare Workers®
TypeScript
2,464
star
14

wildebeest

Wildebeest is an ActivityPub and Mastodon-compatible server
TypeScript
2,042
star
15

gokey

A simple vaultless password manager in Go
Go
1,836
star
16

ebpf_exporter

Prometheus exporter for custom eBPF metrics
C
1,639
star
17

cloudflare-go

The official Go library for the Cloudflare API
Go
1,477
star
18

lol-html

Low output latency streaming HTML parser/rewriter with CSS selector-based API
Rust
1,459
star
19

orange

TypeScript
1,400
star
20

redoctober

Go server for two-man rule style file encryption and decryption.
Go
1,373
star
21

cf-ui

💎 Cloudflare UI Framework
JavaScript
1,297
star
22

sslconfig

Cloudflare's Internet facing SSL configuration
1,287
star
23

foundations

Cloudflare's Rust service foundations library.
Rust
1,273
star
24

next-on-pages

CLI to build and develop Next.js apps for Cloudflare Pages
TypeScript
1,184
star
25

hellogopher

Hellogopher: "just clone and make" your conventional Go project
Makefile
1,153
star
26

production-saas

(WIP) Example SaaS application built in public on the Cloudflare stack!
TypeScript
1,114
star
27

bpftools

BPF Tools - packet analyst toolkit
Python
1,087
star
28

cloudflare-blog

Cloudflare Blog code samples
C
1,065
star
29

templates

A collection of starter templates and examples for Cloudflare Workers and Pages
JavaScript
996
star
30

wrangler-action

🧙‍♀️ easily deploy cloudflare workers applications using wrangler and github actions
TypeScript
993
star
31

circl

CIRCL: Cloudflare Interoperable Reusable Cryptographic Library
Go
970
star
32

cf-terraforming

A command line utility to facilitate terraforming your existing Cloudflare resources.
Go
966
star
33

wirefilter

An execution engine for Wireshark-like filters
Rust
947
star
34

workers-chat-demo

JavaScript
867
star
35

pint

Prometheus rule linter/validator
Go
827
star
36

utahfs

UtahFS is an encrypted storage system that provides a user-friendly FUSE drive backed by cloud storage.
Go
805
star
37

terraform-provider-cloudflare

Cloudflare Terraform Provider
Go
775
star
38

goflow

The high-scalability sFlow/NetFlow/IPFIX collector used internally at Cloudflare.
Go
729
star
39

unsee

Alert dashboard for Prometheus Alertmanager
Go
710
star
40

mitmengine

A MITM (monster-in-the-middle) detection tool. Used to build MALCOLM:
Go
690
star
41

workers-graphql-server

🔥Lightning-fast, globally distributed Apollo GraphQL server, deployed at the edge using Cloudflare Workers
JavaScript
635
star
42

cloudflare-php

PHP library for the Cloudflare v4 API
PHP
616
star
43

react-gateway

Render React DOM into a new context (aka "Portal")
JavaScript
569
star
44

xdpcap

tcpdump like XDP packet capture
Go
567
star
45

ahocorasick

A Golang implementation of the Aho-Corasick string matching algorithm
Go
541
star
46

lua-resty-logger-socket

Raw-socket-based Logger Library for Nginx (based on ngx_lua)
Perl
477
star
47

mmap-sync

Rust library for concurrent data access, using memory-mapped files, zero-copy deserialization, and wait-free synchronization.
Rust
453
star
48

pages-action

JavaScript
450
star
49

speedtest

Component to perform network speed tests against Cloudflare's edge network
JavaScript
435
star
50

stpyv8

Python 3 and JavaScript interoperability. Successor To PyV8 (https://github.com/flier/pyv8)
C++
430
star
51

nginx-google-oauth

Lua module to add Google OAuth to nginx
Lua
425
star
52

worker-typescript-template

ʕ •́؈•̀) TypeScript template for Cloudflare Workers
TypeScript
424
star
53

gokeyless

Go implementation of the keyless protocol
Go
420
star
54

golibs

Various small golang libraries
Go
402
star
55

sandbox

Simple Linux seccomp rules without writing any code
C
385
star
56

mmproxy

mmproxy, the magical PROXY protocol gateway
C
370
star
57

svg-hush

Make it safe to serve untrusted SVG files
Rust
368
star
58

boring

BoringSSL bindings for the Rust programming language.
Rust
357
star
59

cobweb

COBOL to WebAssembly compiler
COBOL
353
star
60

rustwasm-worker-template

A template for kick starting a Cloudflare Worker project using workers-rs. Write your Cloudflare Worker entirely in Rust!
Rust
350
star
61

workers-types

TypeScript type definitions for authoring Cloudflare Workers.
TypeScript
350
star
62

lua-resty-cookie

Lua library for HTTP cookie manipulations for OpenResty/ngx_lua
Perl
347
star
63

cloudflare-ingress-controller

A Kubernetes ingress controller for Cloudflare's Argo Tunnels
Go
344
star
64

node-cloudflare

Node.js API for Client API
JavaScript
335
star
65

serverless-registry

A Docker registry backed by Workers and R2.
TypeScript
327
star
66

cfweb3

JavaScript
313
star
67

workerskv.gui

(WIP) A cross-platform Desktop application for exploring Workers KV Namespace data
Svelte
306
star
68

JSON.is

Open-source documentation for common JSON formats.
JavaScript
302
star
69

sqlalchemy-clickhouse

Python
299
star
70

cloudflare.github.io

Cloudflare ❤️ Open Source
CSS
298
star
71

doom-wasm

Chocolate Doom WebAssembly port with WebSockets support
C
297
star
72

json-schema-tools

Packages for working with JSON Schema and JSON Hyper-Schema
JavaScript
296
star
73

chatgpt-plugin

Build ChatGPT plugins with Cloudflare's Developer Platform 🤖
JavaScript
289
star
74

chanfana

OpenAPI 3 and 3.1 schema generator and validator for Hono, itty-router and more!
TypeScript
284
star
75

tls-tris

crypto/tls, now with 100% more 1.3. THE API IS NOT STABLE AND DOCUMENTATION IS NOT GUARANTEED.
Go
283
star
76

gortr

The RPKI-to-Router server used at Cloudflare
Go
283
star
77

react-modal2

💭 Simple modal component for React.
JavaScript
279
star
78

isbgpsafeyet.com

Is BGP safe yet?
HTML
278
star
79

keyless

Cloudflare's Keyless SSL Server Reference Implementation
C
272
star
80

pp-browser-extension

Client for Privacy Pass protocol providing unlinkable cryptographic tokens
TypeScript
268
star
81

dog

Durable Object Groups
TypeScript
268
star
82

tubular

BSD socket API on steroids
C
261
star
83

go

Go with Cloudflare experimental patches
Go
260
star
84

cloudflare-rs

Rust library for the Cloudflare v4 API
Rust
256
star
85

cloudflare-typescript

The official Typescript library for the Cloudflare API
TypeScript
251
star
86

puppeteer

Puppeteer Core fork that works with Cloudflare Browser Workers
TypeScript
247
star
87

shellflip

Graceful process restarts in Rust
Rust
245
star
88

kv-asset-handler

Routes requests to KV assets
TypeScript
244
star
89

mod_cloudflare

C
243
star
90

semver_bash

Semantic Versioning in Bash
Shell
238
star
91

cfssl_trust

CFSSL's CA trust store repository
Go
226
star
92

doca

A CLI tool that scaffolds API documentation based on JSON HyperSchemas.
JavaScript
224
star
93

alertmanager2es

Receives HTTP webhook notifications from AlertManager and inserts them into an Elasticsearch index for searching and analysis
Go
218
star
94

pmtud

Path MTU daemon - broadcast lost ICMP packets on ECMP networks
C
218
star
95

origin-ca-issuer

Go
216
star
96

worker-template-router

JavaScript
216
star
97

Cloudflare-WordPress

A Cloudflare plugin for WordPress
PHP
215
star
98

cloudflare-docs-engine

A documentation engine built on Gatsby, powering Cloudflare’s docs https://github.com/cloudflare/cloudflare-docs
JavaScript
215
star
99

python-worker-hello-world

Python hello world for Cloudflare Workers
JavaScript
209
star
100

saffron

The cron parser powering Cron Triggers on Cloudflare Workers
Rust
207
star