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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/ARCHITECTURE.md +34 -0
  4. data/CONTRIBUTING.md +7 -0
  5. data/README.md +94 -36
  6. data/Rakefile +1 -1
  7. data/lib/skypager.rb +6 -0
  8. data/lib/skypager/builder.rb +158 -0
  9. data/lib/skypager/builder/server.rb +63 -0
  10. data/lib/skypager/builder/webhook_handler.rb +73 -0
  11. data/lib/skypager/cli/commands/build.rb +48 -0
  12. data/lib/skypager/cli/commands/config.rb +1 -1
  13. data/lib/skypager/cli/commands/setup.rb +13 -0
  14. data/lib/skypager/cli/commands/site.rb +22 -0
  15. data/lib/skypager/cli/commands/sync.rb +1 -1
  16. data/lib/skypager/configuration.rb +32 -7
  17. data/lib/skypager/core_ext.rb +6 -0
  18. data/lib/skypager/data/google_spreadsheet.rb +3 -3
  19. data/lib/skypager/data/source.rb +24 -1
  20. data/lib/skypager/extension.rb +84 -21
  21. data/lib/skypager/proxy.rb +0 -1
  22. data/lib/skypager/router.rb +15 -0
  23. data/lib/skypager/site.rb +104 -21
  24. data/lib/skypager/sync/dropbox/delta.rb +1 -1
  25. data/lib/skypager/sync/folder.rb +7 -1
  26. data/lib/skypager/sync/github.rb +55 -0
  27. data/lib/skypager/version.rb +1 -1
  28. data/skypager.gemspec +21 -13
  29. data/spec/dummy/site-one/.gitignore +18 -0
  30. data/spec/dummy/site-one/Gemfile +14 -0
  31. data/spec/dummy/site-one/config.rb +13 -0
  32. data/spec/dummy/site-one/source/images/background.png +0 -0
  33. data/spec/dummy/site-one/source/images/middleman.png +0 -0
  34. data/spec/dummy/site-one/source/index.html.erb +10 -0
  35. data/spec/dummy/site-one/source/javascripts/all.js +1 -0
  36. data/spec/dummy/site-one/source/layouts/layout.erb +19 -0
  37. data/spec/dummy/site-one/source/stylesheets/all.css +55 -0
  38. data/spec/dummy/site-one/source/stylesheets/normalize.css +375 -0
  39. data/spec/lib/skypager/builder/server_spec.rb +5 -0
  40. data/spec/lib/skypager/configuration_spec.rb +7 -0
  41. data/spec/lib/skypager/site_spec.rb +40 -1
  42. data/spec/lib/skypager_spec.rb +9 -0
  43. data/spec/skypager-test-config.rb.example +22 -0
  44. data/spec/spec_helper.rb +38 -3
  45. data/spec/support/fixtures/cwd_config.json +3 -0
  46. data/spec/support/fixtures/home_config.json +1 -0
  47. metadata +112 -33
  48. data/lib/skypager/build_server.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dd2176972c399bbfec3b8fde460f7592ecfca5a7
4
- data.tar.gz: 08290cdac67c958f31a5e9ca96c2ede7a8827fd8
3
+ metadata.gz: 02aa518232f9553e53c8ecdddbdb55d1118e3697
4
+ data.tar.gz: 269856d146d98cf16044c83874af17ddf705b054
5
5
  SHA512:
6
- metadata.gz: 661f07c8c3beaaf3e466ba68e7fa432c6ec1d62852f02e4bf09762dd97b9db9edd1fd4c3101266462b07bae83097ddf90f7420f7b6fb7f64ccba350c7189e2df
7
- data.tar.gz: 09c044b5e5cdc7554047ad4e1c0827619455edd9b7d9bfd58b8d513f174835ed6c97d67a19845a020d76607367b179197448f23076fc31396cc20c80ec739160
6
+ metadata.gz: c8cd34b2ad8b1bf61a9637094e2bed9fe95c061efb398051783536be1b58cc76590b290791b2a52d7274e9f0f04e8891d9e7298d22624ff5ba8a109f38ef9ff3
7
+ data.tar.gz: 530be140aba47770a87067f8eef4f761d607e5d7c93c9c7a78926420a5a5c66bd4078a8fc1e9431ba67f95a2c9491d29b888ac90e7f7b6123ecd1a10c8c2a94f
data/.gitignore CHANGED
@@ -18,3 +18,4 @@ tmp
18
18
  log/*.log
19
19
  examples/build
20
20
  skypager.json
21
+ spec/skypager-test-config.rb
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
@@ -0,0 +1,7 @@
1
+ ### To work on external integrations
2
+
3
+ You will need to register a few apps with Dropbox, Google, and Github.
4
+
5
+ You will need AWS credentials (secret access key, access key id)
6
+
7
+ You will need a dnsimple username and api token.
data/README.md CHANGED
@@ -1,49 +1,107 @@
1
- # Automate all the boring things
1
+ # Static websites are awesome.
2
2
 
3
- Skypager is a set of tools and API integrations that aims to make
4
- developing and deploying static HTML websites a blast. Static websites
5
- are awesome, and perform far better than dynamically generated websites.
6
- Especially when deployed to a CDN and cached.
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
- A fast page load time is considered to be the single most important factor that effects
9
- conversions.
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
- Of course, people build dynamic websites because they need to be powered
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
- Nobody wants to hand code HTML pages, so we need some form of templates which we can fill with our own
16
- data based on what URL the browser requests.
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
- This is where Skypager comes in. Skypager is a system for reacting to
19
- changes in a variety of data sources, file sharing APIs, or version control systems, and then
20
- automatically triggering builds in your site build tool (Middleman, for
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 can automate setting up your S3 Buckets, Cloudfront CDNs, and
25
- DNS configuration.
20
+ ### Skypager is the best of both worlds
26
21
 
27
- With Skypager, all of the following setups are possible:
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
- - building a photographer website, and allowing the photographer to
30
- store all of her galleries in a Dropbox folder. Every time they add a
31
- new folder or a new file to the folder, Skypager regenerates the
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
- - building a product catalog website powered by a spreadsheet on Google
35
- Drive. Any time your customer adds or edits a row in the spreadsheet,
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
- - building a blog for your creative writer friend by giving them a
39
- Dropbox folder to write markdown. Any time they edit the markdown, or
40
- add a new one? you guessed it.
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
- - Building a Single page javascript app powered by JSON API?
43
- Do you want a static marketing site powered by data in your API or database? Having Rails
44
- generate these pages on the fly and caching them it turns out can be a
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
@@ -16,6 +16,6 @@ require 'rspec/core/rake_task'
16
16
 
17
17
  desc "Run all specs in spec directory (excluding plugin specs)"
18
18
 
19
- RSpec::Core::RakeTask.new(:spec => 'app:db:test:prepare')
19
+ RSpec::Core::RakeTask.new(:spec)
20
20
 
21
21
  task :default => :spec
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