skypager 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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