Skip to content
This repository has been archived by the owner on Jan 12, 2021. It is now read-only.

Commit

Permalink
New: jekyll-app-engine generates app.yaml to host static site on Goog…
Browse files Browse the repository at this point in the history
…le App Engine
  • Loading branch information
James Ramsay committed Mar 1, 2016
1 parent 342c8bd commit eb9c15d
Show file tree
Hide file tree
Showing 35 changed files with 563 additions and 15 deletions.
16 changes: 2 additions & 14 deletions .gitignore
Expand Up @@ -5,32 +5,20 @@
/InstalledFiles
/pkg/
/spec/reports/
/spec/dest/
/spec/examples.txt
/test/tmp/
/test/version_tmp/
/tmp/

## Specific to RubyMotion:
.dat*
.repl_history
build/

## Documentation cache and generated files:
/.yardoc/
/_yardoc/
/doc/
/rdoc/

## Environment normalization:
/.bundle/
/vendor/bundle
/lib/bundler/man/

# for a library or gem, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# Gemfile.lock
# .ruby-version
# .ruby-gemset
Gemfile.lock

# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc
5 changes: 5 additions & 0 deletions .travis.yml
@@ -0,0 +1,5 @@
language: ruby
rvm:
- 2.2.3
script:
- rake
6 changes: 6 additions & 0 deletions Gemfile
@@ -0,0 +1,6 @@
source "https://rubygems.org"
gemspec

if ENV["JEKYLL_VERSION"]
gem "jekyll", "~> #{ENV["JEKYLL_VERSION"]}"
end
7 changes: 7 additions & 0 deletions History.markdown
@@ -0,0 +1,7 @@
## 0.1.0 / 2016-01-01

* Birthday!

## 0.0.0 / 2016-02-24

* Alpha
35 changes: 34 additions & 1 deletion README.md
@@ -1 +1,34 @@
# jekyll-app-engine
# jekyll-app-engine: deploy your site to Google App Engine

[![Build Status](https://img.shields.io/travis/jamesramsay/jekyll-app-engine/master.svg)](https://travis-ci.org/jamesramsay/jekyll-app-engine)
[![Version](https://img.shields.io/gem/v/jekyll-app-engine.svg)](https://rubygems.org/gems/jekyll-app-engine)

`jekyll-app-engine` makes it easy to deploy your jekyll site to Google App Engine by generating handlers for your `app.yaml`.

Using Google App Engine to host your Jekyll site has the following benefits:

- HTTPS
- HTTP cache control for pages and assets
- HTTP/2 support including PUSH
- Use custom jekyll plugins not supported by Github Pages
- Google CDN

Limitations:

- 404 handling not customisable

## Usage

Add `gem jekyll-app-engine` to your Gemfile:

```
source 'https://rubygems.org'
gem 'github-pages'
gem 'jekyll-app-engine'
```

Add configs...

## Documentation

todo
6 changes: 6 additions & 0 deletions Rakefile
@@ -0,0 +1,6 @@
require "bundler/gem_tasks"
require 'rspec/core/rake_task'

RSpec::Core::RakeTask.new(:spec)

task :default => :spec
21 changes: 21 additions & 0 deletions jekyll-app-engine.gemspec
@@ -0,0 +1,21 @@
# coding: utf-8

Gem::Specification.new do |spec|
spec.name = "jekyll-app-engine"
spec.summary = "Generator for Google App Engine Handlers."
spec.version = "0.0.1"
spec.authors = ["James Ramsay"]
spec.email = "git@jwr.vc "
spec.homepage = "http://github.com/jamesramsay/jekyll-app-engine"
spec.licenses = ["MIT"]

spec.files = `git ls-files -z`.split("\x0")
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]

spec.add_development_dependency "jekyll", ">= 2.0"
spec.add_development_dependency "rspec", "~> 3.0"
spec.add_development_dependency "rake"
spec.add_development_dependency "bundler", "~> 1.6"
end
1 change: 1 addition & 0 deletions lib/app.yaml
@@ -0,0 +1 @@
{{ content }}
163 changes: 163 additions & 0 deletions lib/jekyll-app-engine.rb
@@ -0,0 +1,163 @@
require 'fileutils'

module Jekyll

class GoogleYAMLTree < Psych::Visitors::YAMLTree
def accept target
if target.respond_to?(:to_yaml)
begin
loc = target.method(:to_yaml).source_location.first
if loc !~ /(syck\/rubytypes.rb|psych\/core_ext.rb)/
unless target.respond_to?(:encode_with)
if $VERBOSE
warn "implementing to_yaml is deprecated, please implement \"encode_with\""
end

target.to_yaml(:nodump => true)
end
end
rescue
# public_method or source_location might be overridden,
# and it's OK to skip it since it's only to emit a warning
end
end

if target.respond_to?(:encode_with)
dump_coder target
else
send(@dispatch_cache[target.class], target)
end
end
end

class PageWithoutAFile < Page
def read_yaml(*)
@data ||= {}
end
end

class JekyllAppEngine < Jekyll::Generator
safe true
priority :lowest

# Main plugin action, called by Jekyll-core
def generate(site)
@site = site
@app_engine = site.config["app_engine"]

unless app_yaml_exists?
unless @app_engine["base"] or source_partial_exists?
raise "App engine base configration not found"
end

write
@site.keep_files ||= []
@site.keep_files << "app.yaml"
end
end

# Checks if a optional _app.yaml partial already exists
def source_partial_exists?
if @site.respond_to?(:in_source_dir)
File.exists? @site.in_source_dir("_app.yaml")
else
File.exists? Jekyll.sanitized_path(@site.source, "_app.yaml")
end
end

# Path to optional _app.yaml partial
def source_path
if @site.respond_to?(:in_source_dir)
@site.in_source_dir("_app.yaml")
else
Jekyll.sanitized_path(@site.source, "_app.yaml")
end
end

# Destination for app.yaml file within the site source directory
def destination_path
if @site.respond_to?(:in_dest_dir)
@site.in_dest_dir("app.yaml")
else
Jekyll.sanitized_path(@site.dest, "app.yaml")
end
end

def write
FileUtils.mkdir_p File.dirname(destination_path)
File.open(destination_path, 'w') { |f| f.write(app_yaml_content) }
end

def app_yaml_content
builder = GoogleYAMLTree.create
builder << generate_app_engine_yaml

app_yaml = PageWithoutAFile.new(@site, File.dirname(__FILE__), "", "app.yaml")
app_yaml.content = builder.tree.yaml
app_yaml.data["layout"] = nil
app_yaml.render({}, @site.site_payload)
return app_yaml.output
end

def generate_app_engine_yaml
if source_partial_exists?
app_yaml = YAML.load_file(source_path)
else
app_yaml = @app_engine["base"].dup
end

app_yaml["handlers"] ||= []

generate_handlers("posts", @site.posts.docs).each { |handler| app_yaml["handlers"] << handler }
generate_handlers("pages", @site.pages).each { |handler| app_yaml["handlers"] << handler }

@site.collections.each_pair do |label, collection|
unless label == "posts"
generate_handlers("collections", collection.docs).each { |handler| app_yaml["handlers"] << handler }
end
end

generate_handlers("static", @site.static_files).each { |handler| app_yaml["handlers"] << handler }

return app_yaml
end

def generate_handlers(content_type, collection)
handlers = []

handler_template = @app_engine["handlers"][content_type] || {}
if handler_template.kind_of?(Array) or handler_template.has_key?("url")
handlers << handler_template
else
collection.each do |doc|
handler = {
"url" => doc.url,
"static_files" => doc.destination("").sub("#{Dir.pwd}/", ""),
"upload" => doc.destination("").sub("#{Dir.pwd}/", "")
}
handlers << handler.merge!(handler_template.dup).merge!(document_overrides(doc))
end
end

return handlers
end

# Document specific app.yaml configuration provided in yaml frontmatter
def document_overrides(document)
if document.respond_to?(:data) and document.data.has_key?("app_engine")
document.data.fetch("app_engine")
else
{}
end
end

# Checks if a app.yaml already exists in the site source
def app_yaml_exists?
if @site.respond_to?(:in_source_dir)
File.exists? @site.in_source_dir("app.yaml")
else
File.exists? Jekyll.sanitized_path(@site.source, "app.yaml")
end
end
end
end
3 changes: 3 additions & 0 deletions script/bootstrap
@@ -0,0 +1,3 @@
#!/bin/sh

bundle install
4 changes: 4 additions & 0 deletions script/cibuild
@@ -0,0 +1,4 @@
#!/bin/sh
set -e

bundle exec rspec
34 changes: 34 additions & 0 deletions script/console
@@ -0,0 +1,34 @@
#! /usr/bin/env ruby

def relative_to_root(path)
File.expand_path(path, File.dirname(File.dirname(__FILE__)))
end

require 'jekyll'
require relative_to_root('lib/jekyll-app-engine.rb')
require 'pry-debugger'

SOURCE_DIR = relative_to_root('spec/fixtures')
DEST_DIR = relative_to_root('spec/dest')

def source_dir(*files)
File.join(SOURCE_DIR, *files)
end

def dest_dir(*files)
File.join(DEST_DIR, *files)
end

def config(overrides = {})
Jekyll.configuration({
"source" => source_dir,
"destination" => dest_dir,
"url" => "http://example.org"
}).merge(overrides)
end

def site(configuration = config)
Jekyll::Site.new(configuration)
end

binding.pry
7 changes: 7 additions & 0 deletions script/release
@@ -0,0 +1,7 @@
#!/bin/sh
# Tag and push a release.

set -e

script/cibuild
bundle exec rake release
26 changes: 26 additions & 0 deletions spec/fixtures/_config.yml
@@ -0,0 +1,26 @@
timezone: UTC

defaults:
-
scope:
path: ""
type: page
values:
layout: some_default

app_engine:
base:
runtime: go
api_version: go1
default_expiration: 300s
handlers:
posts:
http_headers:
Link: "</static/css/style.css>; rel=preload; as=style"
statics:
- url: /images/
static_dir: _site/images
expiration: 4m
- url: /styles/
static_dir: _site/styles
expiration: 8m
4 changes: 4 additions & 0 deletions spec/fixtures/_layouts/some_default.html
@@ -0,0 +1,4 @@
---
---
THIS IS MY LAYOUT
{{ content }}
5 changes: 5 additions & 0 deletions spec/fixtures/_my_collection/custom_permalink.md
@@ -0,0 +1,5 @@
---
permalink: /permalink/
---

# Custom permalink
5 changes: 5 additions & 0 deletions spec/fixtures/_my_collection/custom_permalink_2.md
@@ -0,0 +1,5 @@
---
permalink: /permalink/unique_name.html
---

# Unique html name
4 changes: 4 additions & 0 deletions spec/fixtures/_my_collection/test.html
@@ -0,0 +1,4 @@
---
---

This is just a test.
5 changes: 5 additions & 0 deletions spec/fixtures/_my_collection/this-has-non-standard-chars.md
@@ -0,0 +1,5 @@
---
permalink: this url has an ümlaut
---

# URL contains characters that need to be URI encoded
4 changes: 4 additions & 0 deletions spec/fixtures/_other_things/test2.html
@@ -0,0 +1,4 @@
---
---

This file shouldn't show up in the app.yaml.

0 comments on commit eb9c15d

Please sign in to comment.