skypager 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/ARCHITECTURE.md +34 -0
- data/CONTRIBUTING.md +7 -0
- data/README.md +94 -36
- data/Rakefile +1 -1
- data/lib/skypager.rb +6 -0
- data/lib/skypager/builder.rb +158 -0
- data/lib/skypager/builder/server.rb +63 -0
- data/lib/skypager/builder/webhook_handler.rb +73 -0
- data/lib/skypager/cli/commands/build.rb +48 -0
- data/lib/skypager/cli/commands/config.rb +1 -1
- data/lib/skypager/cli/commands/setup.rb +13 -0
- data/lib/skypager/cli/commands/site.rb +22 -0
- data/lib/skypager/cli/commands/sync.rb +1 -1
- data/lib/skypager/configuration.rb +32 -7
- data/lib/skypager/core_ext.rb +6 -0
- data/lib/skypager/data/google_spreadsheet.rb +3 -3
- data/lib/skypager/data/source.rb +24 -1
- data/lib/skypager/extension.rb +84 -21
- data/lib/skypager/proxy.rb +0 -1
- data/lib/skypager/router.rb +15 -0
- data/lib/skypager/site.rb +104 -21
- data/lib/skypager/sync/dropbox/delta.rb +1 -1
- data/lib/skypager/sync/folder.rb +7 -1
- data/lib/skypager/sync/github.rb +55 -0
- data/lib/skypager/version.rb +1 -1
- data/skypager.gemspec +21 -13
- data/spec/dummy/site-one/.gitignore +18 -0
- data/spec/dummy/site-one/Gemfile +14 -0
- data/spec/dummy/site-one/config.rb +13 -0
- data/spec/dummy/site-one/source/images/background.png +0 -0
- data/spec/dummy/site-one/source/images/middleman.png +0 -0
- data/spec/dummy/site-one/source/index.html.erb +10 -0
- data/spec/dummy/site-one/source/javascripts/all.js +1 -0
- data/spec/dummy/site-one/source/layouts/layout.erb +19 -0
- data/spec/dummy/site-one/source/stylesheets/all.css +55 -0
- data/spec/dummy/site-one/source/stylesheets/normalize.css +375 -0
- data/spec/lib/skypager/builder/server_spec.rb +5 -0
- data/spec/lib/skypager/configuration_spec.rb +7 -0
- data/spec/lib/skypager/site_spec.rb +40 -1
- data/spec/lib/skypager_spec.rb +9 -0
- data/spec/skypager-test-config.rb.example +22 -0
- data/spec/spec_helper.rb +38 -3
- data/spec/support/fixtures/cwd_config.json +3 -0
- data/spec/support/fixtures/home_config.json +1 -0
- metadata +112 -33
- data/lib/skypager/build_server.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02aa518232f9553e53c8ecdddbdb55d1118e3697
|
4
|
+
data.tar.gz: 269856d146d98cf16044c83874af17ddf705b054
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8cd34b2ad8b1bf61a9637094e2bed9fe95c061efb398051783536be1b58cc76590b290791b2a52d7274e9f0f04e8891d9e7298d22624ff5ba8a109f38ef9ff3
|
7
|
+
data.tar.gz: 530be140aba47770a87067f8eef4f761d607e5d7c93c9c7a78926420a5a5c66bd4078a8fc1e9431ba67f95a2c9491d29b888ac90e7f7b6123ecd1a10c8c2a94f
|
data/.gitignore
CHANGED
data/ARCHITECTURE.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
### Middleman Extension
|
2
|
+
|
3
|
+
The middleman extension provides configuration to do things like
|
4
|
+
declare mappings between dropbox folders and your local folders, or to
|
5
|
+
define an external data source based on a google spreadsheet.
|
6
|
+
|
7
|
+
the extension takes care of keeping everything in sync for you between
|
8
|
+
builds, or more frequently in development
|
9
|
+
|
10
|
+
### Skypager CLI
|
11
|
+
|
12
|
+
Creates new data sources, shared folders, etc. Walks you through
|
13
|
+
setting up AWS, DNSimple, etc. Can be used to deploy.
|
14
|
+
|
15
|
+
### Skypager Site
|
16
|
+
|
17
|
+
This represents a single middleman application which uses the skypager
|
18
|
+
etension.
|
19
|
+
|
20
|
+
### Syncable Folders
|
21
|
+
|
22
|
+
syncable folders (folders which are tied to external file hosting
|
23
|
+
services, such as Dropbox or Google Drive)
|
24
|
+
|
25
|
+
### Data Sources
|
26
|
+
|
27
|
+
data sources .external services such as Google Drive, or Dropox, will
|
28
|
+
hold spreadsheets or csv files, which are basically just editable
|
29
|
+
tables. These get pulled in to the `data` folder and stored as json
|
30
|
+
files.
|
31
|
+
|
32
|
+
middleman provides that json files stored in the data folder, will be
|
33
|
+
available to the config.rb as well as the template helpers. this is
|
34
|
+
how skypager data sources get used by the middleman project.
|
data/CONTRIBUTING.md
ADDED
data/README.md
CHANGED
@@ -1,49 +1,107 @@
|
|
1
|
-
#
|
1
|
+
# Static websites are awesome.
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
This is a fact: Static websites with no backend server are the cheapest, fastest,
|
4
|
+
and most secure websites out there. You can compress things to the max,
|
5
|
+
cache them on a CDN distributed across the entire world, and guarantee
|
6
|
+
the fastest page loads time for your users.
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
If you are starting out developing a website or a web based application, constraining yourself to
|
9
|
+
keeping as much of your user facing web assets static will pay off.
|
10
10
|
|
11
|
-
|
12
|
-
by a Content Management System and a database, and supported by a version
|
13
|
-
control system, among many other completely valid reasons.
|
11
|
+
### Dynamic sites are practical
|
14
12
|
|
15
|
-
|
16
|
-
|
13
|
+
But a static HTML only website with no backend or content management just doesn't suit the needs of most people, so people
|
14
|
+
turn to hosted solutions like Wordpress or Drupal, maybe they develop their own Rails app.
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
example.) Skypager then takes the static build, and deploys it to an Amazon S3
|
22
|
-
Bucket, and triggers expirations of your Cloudfront CDN or other Caches.
|
16
|
+
The problem with dynamic sites powered by servers and application code and databases is you need to scale them,
|
17
|
+
secure them, host them, or hire people to do those things for you. Or you can pay for hosting for some generic
|
18
|
+
content management platform, but you'll be constrained by these platforms and limited in the ways you can customize them.
|
23
19
|
|
24
|
-
Skypager
|
25
|
-
DNS configuration.
|
20
|
+
### Skypager is the best of both worlds
|
26
21
|
|
27
|
-
|
22
|
+
If we eliminate the sites that developers make for themselves and for
|
23
|
+
other developers, most dynamic websites are going to be relying on content that
|
24
|
+
comes from people who aren't developers. Whether that is blog posts,
|
25
|
+
marketing copy, landing pages, images, videos, or a large dataset which
|
26
|
+
contains records that will be represented by their own URL.
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
static pages and pushes the assets to S3.
|
28
|
+
What tools are our non-developer teammates already using? They're using Dropbox, Google
|
29
|
+
Drive, Excel Spreadsheets, Word Documents, Photos, Videos, and things of
|
30
|
+
that nature.
|
33
31
|
|
34
|
-
|
35
|
-
|
36
|
-
Skypager generates the product page and deploys it for you.
|
32
|
+
Skypager provides ways to sync up with these other tools, and regenerate
|
33
|
+
your site's content whenever changes are made to these file systems.
|
37
34
|
|
38
|
-
|
39
|
-
|
40
|
-
|
35
|
+
This opens up a lot of interesting possibilities for a wide range of
|
36
|
+
different kinds of websites, and ways of collaborating with people to
|
37
|
+
build them.
|
41
38
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
terrible approach. Skypager can watch a REST endpoint in your app for
|
46
|
-
changes, or you can POST to the Skypager Build Server's Webhooks and
|
47
|
-
trigger a build on your own.
|
39
|
+
Best of all, you get the complete control and freedom provided to
|
40
|
+
you by the Ruby programming language and the many great site template authoring
|
41
|
+
tools provided by the community.
|
48
42
|
|
43
|
+
### What about user generated content?
|
49
44
|
|
45
|
+
Serverless apps which rely on storing and user generated content will need to get
|
46
|
+
creative with how they handle these requirements. When you are constraining yourself to
|
47
|
+
using a static site, this is one of the tradeoffs.
|
48
|
+
|
49
|
+
We have a number of guides and examples for how you can accomplish this. Coming soon.
|
50
|
+
|
51
|
+
### Hassle free deployment, DNS Configuration, and CDN hosting setup
|
52
|
+
|
53
|
+
Skypager automates all of the boring but necessary steps that somebody
|
54
|
+
needs to take if they wanted to configure their own Static website
|
55
|
+
hosting using Amazon S3, Cloudfront, and it even takes care of the DNS configuration
|
56
|
+
you'll need to do to make this setup work on your own domain.
|
57
|
+
|
58
|
+
### Sync with Dropbox, Google Drive, and Github
|
59
|
+
|
60
|
+
Store your source code on Github so your developer can work on it.
|
61
|
+
|
62
|
+
Store your photo library on Dropbox or Google Drive so your designer
|
63
|
+
and photographer can manage those assets using the tools they work with.
|
64
|
+
|
65
|
+
Store an excel spreadsheet containing all of your product information on Dropbox,
|
66
|
+
or in a Google spreadsheet, so people from your business team can manage
|
67
|
+
that content.
|
68
|
+
|
69
|
+
Skypager will make sure these external sources of content are always
|
70
|
+
available, and incorporate them into the latest build of the site.
|
71
|
+
|
72
|
+
### Powered by Middleman
|
73
|
+
|
74
|
+
Skypager is an extension to the [Middleman](http://middlemanapp.com) static site generator.
|
75
|
+
Middleman allows for you to develop your site using templates, and
|
76
|
+
enhanced CSS and Javascript authoring languages like Sass, Less,
|
77
|
+
or Coffeescript.
|
78
|
+
|
79
|
+
### Getting Started: Standalone Mode
|
80
|
+
|
81
|
+
Skypager in standlone mode is what you would want to use if you are
|
82
|
+
working on a single site, and you are comfortable with the command line
|
83
|
+
and ruby. Instructions for doing this can be found in our [Getting
|
84
|
+
Started Guide](https://github.com/architects/skypager)
|
85
|
+
|
86
|
+
### The Skypager Build Server
|
87
|
+
|
88
|
+
If you are managing a large number of Skypager sites, and would like to set it
|
89
|
+
up so that all of your sites stay up to date and in sync, you can run your
|
90
|
+
own instance of the Skypager Build Server.
|
91
|
+
|
92
|
+
The build server will do things like: accept webhooks from Github,
|
93
|
+
Dropbox, or Google Drive, which will happen whenever content changes.
|
94
|
+
|
95
|
+
This will trigger a re-build of your site, and the updated content gets automatically pushed
|
96
|
+
to the hosting environment and to the CDN.
|
97
|
+
|
98
|
+
### Skypager.io: Hosted option with a Web UI
|
99
|
+
|
100
|
+
The easiest way to use Skypager in production is to subscribe to the Skypager.io
|
101
|
+
hosting service, which provides you with a web based front end for
|
102
|
+
managing all of your sites and handles all of the hosting service
|
103
|
+
configuration and such for you.
|
104
|
+
|
105
|
+
Just point us to the source code, whether it is on Dropbox or Github,
|
106
|
+
and use the rest of the features which you can configure using our webfront end, or our
|
107
|
+
Command line interface (CLI).
|
data/Rakefile
CHANGED
data/lib/skypager.rb
CHANGED
@@ -59,6 +59,11 @@ module Skypager
|
|
59
59
|
Skypager::Sync::Dropbox.client
|
60
60
|
end
|
61
61
|
|
62
|
+
def self.github
|
63
|
+
require 'octokit' unless defined?(Octokit)
|
64
|
+
Skypager::Sync::Github.client
|
65
|
+
end
|
66
|
+
|
62
67
|
def self.dns
|
63
68
|
require 'dnsimple'
|
64
69
|
require 'skypager/dns'
|
@@ -105,5 +110,6 @@ require 'skypager/sync/dropbox/delta'
|
|
105
110
|
require 'skypager/sync/google'
|
106
111
|
require 'skypager/sync/folder'
|
107
112
|
require 'skypager/sync/amazon'
|
113
|
+
require 'skypager/sync/github'
|
108
114
|
require 'skypager/dns'
|
109
115
|
require 'skypager/extension'
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module Skypager
|
2
|
+
class Builder
|
3
|
+
|
4
|
+
attr_reader :app,
|
5
|
+
:source,
|
6
|
+
:logger,
|
7
|
+
:options
|
8
|
+
|
9
|
+
def initialize(app, options={})
|
10
|
+
@app = app
|
11
|
+
@options = options.reverse_merge(clean: true, verbose: true)
|
12
|
+
@source_dir = app.source_path
|
13
|
+
@build_dir = app.build_path
|
14
|
+
@to_clean = Set.new
|
15
|
+
|
16
|
+
@logger = app.logger
|
17
|
+
@rack = ::Rack::Test::Session.new(app.class.to_rack_app)
|
18
|
+
end
|
19
|
+
|
20
|
+
def build(force=false)
|
21
|
+
return unless force || needs_build?
|
22
|
+
|
23
|
+
queue_current_paths if should_clean?
|
24
|
+
execute!
|
25
|
+
clean! if should_clean?
|
26
|
+
|
27
|
+
app.site.requires_build!(false)
|
28
|
+
app.deploy! if app.auto_deploy?
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def clean!
|
34
|
+
@to_clean.each do |f|
|
35
|
+
FileUtils.rm_rf(f)
|
36
|
+
end
|
37
|
+
|
38
|
+
Dir[@build_dir.join('**', '*')].select { |d| File.directory?(d) }.each do |d|
|
39
|
+
FileUtils.rm_rf(d) if Pathname(d).children.empty?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def should_clean?
|
44
|
+
options[:clean]
|
45
|
+
end
|
46
|
+
|
47
|
+
def queue_current_paths
|
48
|
+
return unless app.build_path.exist?
|
49
|
+
|
50
|
+
paths = ::Middleman::Util.all_files_under(app.build_path).map(&:realpath).select(&:file?)
|
51
|
+
|
52
|
+
@to_clean += paths.select do |path|
|
53
|
+
path.to_s !~ /\/\./ || path.to_s =~ /\.(htaccess|htpasswd)/
|
54
|
+
end
|
55
|
+
|
56
|
+
return unless RUBY_PLATFORM =~ /darwin/
|
57
|
+
|
58
|
+
# handle UTF-8-MAC filename on MacOS
|
59
|
+
@to_clean = @to_clean.map { |path| path.to_s.encode('UTF-8', 'UTF-8-MAC') }
|
60
|
+
end
|
61
|
+
|
62
|
+
def needs_build?
|
63
|
+
@needs_build = app.data_sources.values.any? do |ds|
|
64
|
+
ds.fresh_on_server?
|
65
|
+
end
|
66
|
+
|
67
|
+
# TODO
|
68
|
+
# Add support for google drive folders, github folder changes
|
69
|
+
@needs_build = @needs_build || app.syncables.any? do |syncable|
|
70
|
+
syncable.dropbox? && syncable.has_remote_changes?
|
71
|
+
end
|
72
|
+
|
73
|
+
@needs_build
|
74
|
+
end
|
75
|
+
|
76
|
+
def execute!
|
77
|
+
sort_order = %w(.png .jpeg .jpg .gif .bmp .svg .svgz .ico .webp .woff .otf .ttf .eot .js .css)
|
78
|
+
|
79
|
+
app.sitemap.resources.select do |resource|
|
80
|
+
resource.ext == '.css'
|
81
|
+
end.each(&method(:build_resource))
|
82
|
+
|
83
|
+
app.files.find_new_files((@source_dir + app.images_dir).relative_path_from(app.root_path))
|
84
|
+
app.sitemap.ensure_resource_list_updated!
|
85
|
+
|
86
|
+
resources = app.sitemap.resources.sort_by do |r|
|
87
|
+
sort_order.index(r.ext) || 100
|
88
|
+
end
|
89
|
+
|
90
|
+
if @build_dir.expand_path.relative_path_from(@source_dir).to_s =~ /\A[.\/]+\Z/
|
91
|
+
raise ":build_dir (#{@build_dir}) cannot be a parent of :source_dir (#{@source_dir})"
|
92
|
+
end
|
93
|
+
|
94
|
+
resources.reject do |resource|
|
95
|
+
resource.ext == '.css'
|
96
|
+
end.each(&method(:build_resource))
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
def build_resource(resource)
|
101
|
+
return if options[:glob] && !File.fnmatch(options[:glob], resource.destination_path)
|
102
|
+
|
103
|
+
output_path = render_to_file(resource)
|
104
|
+
|
105
|
+
return unless should_clean? && output_path.exist?
|
106
|
+
|
107
|
+
if RUBY_PLATFORM =~ /darwin/
|
108
|
+
# handle UTF-8-MAC filename on MacOS
|
109
|
+
|
110
|
+
@to_clean.delete(output_path.realpath.to_s.encode('UTF-8', 'UTF-8-MAC'))
|
111
|
+
else
|
112
|
+
@to_clean.delete(output_path.realpath)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def render_to_file(resource)
|
117
|
+
output_file = @build_dir + resource.destination_path.gsub('%20', ' ')
|
118
|
+
|
119
|
+
if resource.binary?
|
120
|
+
if !output_file.exist?
|
121
|
+
#base.say_status :create, output_file, :green
|
122
|
+
elsif FileUtils.compare_file(resource.source_file, output_file)
|
123
|
+
#base.say_status :identical, output_file, :blue
|
124
|
+
return output_file
|
125
|
+
else
|
126
|
+
#base.say_status :update, output_file, :yellow
|
127
|
+
end
|
128
|
+
|
129
|
+
output_file.dirname.mkpath
|
130
|
+
FileUtils.cp(resource.source_file, output_file)
|
131
|
+
else
|
132
|
+
begin
|
133
|
+
response = @rack.get(URI.escape(resource.request_path))
|
134
|
+
|
135
|
+
if response.status == 200
|
136
|
+
Pathname(output_file).open("w+") {|fh| fh.write binary_encode(response.body) }
|
137
|
+
else
|
138
|
+
handle_error(output_file, response.body)
|
139
|
+
end
|
140
|
+
rescue => e
|
141
|
+
handle_error(output_file, "#{e}\n#{e.backtrace.join("\n")}", e)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
output_file
|
146
|
+
end
|
147
|
+
|
148
|
+
def handle_error(file_name, response, *args)
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
def binary_encode(string)
|
153
|
+
string.force_encoding('ascii-8bit') if string.respond_to?(:force_encoding)
|
154
|
+
string
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Skypager::Builder::Server
|
2
|
+
#
|
3
|
+
# A Rack mountable server which can receive Webhook notifications
|
4
|
+
# from services like Dropbox, Google Drive, or our own Rest APIs
|
5
|
+
# and trigger builds for the various Skypager::Site that are referenced
|
6
|
+
# in the Skypager::Site.directory
|
7
|
+
#
|
8
|
+
# Currently, the Builder::Server just finds sites and marks them as requiring
|
9
|
+
# a build. Some external process will poll the sites directory to find the sites
|
10
|
+
# which require a build, and run that for us. (Cron, perhaps)
|
11
|
+
module Skypager
|
12
|
+
class Builder
|
13
|
+
class Server
|
14
|
+
attr_reader :options
|
15
|
+
|
16
|
+
def initialize(*args)
|
17
|
+
@options = args.extract_options!
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(env)
|
21
|
+
request = Rack::Request.new(env)
|
22
|
+
respond_to(request)
|
23
|
+
rescue => e
|
24
|
+
[500, {}, [e.message]]
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def webhooks
|
30
|
+
require "skypager/builder/webhook_handler" unless defined?(Skypager::Builder::WebhookHandler)
|
31
|
+
Skypager::Builder::WebhookHandler
|
32
|
+
end
|
33
|
+
|
34
|
+
def respond_to_webhook(request)
|
35
|
+
case
|
36
|
+
when request.path.match(/dropbox/)
|
37
|
+
puts "== Found dropbox webhook"
|
38
|
+
webhooks.handle(:dropbox, request)
|
39
|
+
when request.path.match(/github/)
|
40
|
+
puts "== Found github webhook"
|
41
|
+
webhooks.handle(:github, request)
|
42
|
+
when request.path.match(/google/)
|
43
|
+
puts "== Found google webhook"
|
44
|
+
webhooks.handle(:google, request)
|
45
|
+
else
|
46
|
+
puts "== Found custom webhook"
|
47
|
+
webhooks.handle(:custom, request)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def respond_to(request)
|
52
|
+
puts "== Responding to: #{ request.path }"
|
53
|
+
case
|
54
|
+
when request.path.match(/hooks/)
|
55
|
+
puts "== Found hooks path"
|
56
|
+
respond_to_webhook(request)
|
57
|
+
else
|
58
|
+
[404, {}, [""]]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|