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
@@ -0,0 +1,73 @@
|
|
1
|
+
module Skypager::Builder::WebhookHandler
|
2
|
+
def self.handle(service_identifier=:custom, request)
|
3
|
+
unless respond_to?("handle_#{service_identifier}")
|
4
|
+
return [404, {}, [""]]
|
5
|
+
end
|
6
|
+
|
7
|
+
puts "== Handling #{ service_identifier }: #{ request.path } #{ request.params }"
|
8
|
+
send("handle_#{service_identifier}", request)
|
9
|
+
end
|
10
|
+
|
11
|
+
# The Dropbox API requires us to support
|
12
|
+
# two different types of Webhook requests.
|
13
|
+
#
|
14
|
+
# One will include the UID of the users who have changes.
|
15
|
+
# We will need to find all of the skypager sites which are
|
16
|
+
# registered against this UID in order to find the credentials
|
17
|
+
# needed to make a delta request to the API.
|
18
|
+
#
|
19
|
+
# We will then use the paths we find in the delta, to find
|
20
|
+
# the specific site that we need to mark for building
|
21
|
+
def self.handle_dropbox(request)
|
22
|
+
puts "== Dropbox handler initiated"
|
23
|
+
body = request.body.read
|
24
|
+
|
25
|
+
puts "== Request body: #{ body }"
|
26
|
+
puts "== Params: #{ request.params.inspect }"
|
27
|
+
|
28
|
+
begin
|
29
|
+
if request.params['challenge']
|
30
|
+
puts "== challenge request"
|
31
|
+
response = "#{request.params['challenge']}"
|
32
|
+
elsif body.length > 0
|
33
|
+
data = JSON.parse(body) rescue nil
|
34
|
+
data = data.to_mash if data.respond_to?(:to_mash)
|
35
|
+
|
36
|
+
Array(data.delta.users).each do |uid|
|
37
|
+
puts "== Attempting to find a site for #{ uid }"
|
38
|
+
sites = Skypager::Site.find_sites_for_dropbox_uid(uid)
|
39
|
+
|
40
|
+
if sites.length > 0
|
41
|
+
sites.each do |site|
|
42
|
+
puts "== Marking site for build: #{ site.name }"
|
43
|
+
site.requires_build!
|
44
|
+
end
|
45
|
+
else
|
46
|
+
puts "== Could not find site for uid: #{ uid }"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
response = data.delta.users.to_json
|
51
|
+
end
|
52
|
+
rescue => e
|
53
|
+
response = "== Error: #{ e.message }"
|
54
|
+
puts response
|
55
|
+
end
|
56
|
+
|
57
|
+
[200, {}, [response]]
|
58
|
+
end
|
59
|
+
|
60
|
+
# TODO
|
61
|
+
# Investigate the makeup of google drive webhook request
|
62
|
+
def self.handle_google(request)
|
63
|
+
end
|
64
|
+
|
65
|
+
# TODO
|
66
|
+
# Presumably we will get commit notifications from Github,
|
67
|
+
# and will find the site that matches the notification
|
68
|
+
def self.handle_github(request)
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.handle_custom(request)
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
if defined?(Slim::Engine)
|
2
|
+
Slim::Engine.disable_option_validator!
|
3
|
+
end
|
4
|
+
|
5
|
+
command 'build' do |c|
|
6
|
+
c.syntax = "skypager build"
|
7
|
+
c.description = "performs a build on the site"
|
8
|
+
|
9
|
+
c.option "--force", nil, 'Whether or not we should force a build'
|
10
|
+
|
11
|
+
c.action do |args, options|
|
12
|
+
require 'rack/test'
|
13
|
+
require 'skypager/builder'
|
14
|
+
|
15
|
+
app = Skypager.app
|
16
|
+
builder = Skypager::Builder.new(app, clean: true, verbose: true)
|
17
|
+
|
18
|
+
if app.site.requires_build? || options.force.present?
|
19
|
+
puts "== Building site: #{ app.site.name }"
|
20
|
+
builder.build(options.force.present?)
|
21
|
+
else
|
22
|
+
puts "== Site is not marked to be built. Skipping unless the --force flag is passed"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
command 'run build server' do |c|
|
28
|
+
c.syntax = 'skypager run build server [OPTIONS]'
|
29
|
+
c.description = 'run the skypager webhook handler'
|
30
|
+
|
31
|
+
c.option '--port', Fixnum, 'What port should this run on?'
|
32
|
+
c.option '--host', String, 'What host should this run on?'
|
33
|
+
|
34
|
+
c.action do |args, options|
|
35
|
+
require 'rack'
|
36
|
+
require 'skypager/builder/server'
|
37
|
+
|
38
|
+
opts = {
|
39
|
+
Host: options.host || '127.0.0.1',
|
40
|
+
Port: options.port || 3000
|
41
|
+
}
|
42
|
+
|
43
|
+
Rack::Handler::WEBrick.run(Skypager::Builder::Server.new, opts) do |server|
|
44
|
+
[:INT, :TERM].each { |sig| trap(sig) { server.stop } }
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -26,7 +26,7 @@ end
|
|
26
26
|
|
27
27
|
command 'config get' do |c|
|
28
28
|
c.syntax = 'skypager config:get [options]'
|
29
|
-
c.description = '
|
29
|
+
c.description = 'view a skypager configuration setting'
|
30
30
|
|
31
31
|
c.action do |args, _options|
|
32
32
|
Skypager::Configuration.initialize!
|
@@ -122,3 +122,16 @@ command 'setup google' do |c|
|
|
122
122
|
client_secret: options.client_secret)
|
123
123
|
end
|
124
124
|
end
|
125
|
+
|
126
|
+
command 'setup github' do |c|
|
127
|
+
c.syntax = 'skypager setup github [options]'
|
128
|
+
c.description = 'Setup github syncing'
|
129
|
+
|
130
|
+
c.option '--github-access-token STRING', String, 'What is the Github access token?'
|
131
|
+
|
132
|
+
c.action do |args, options|
|
133
|
+
Skypager::Sync::Github.setup(github_access_token: options.github_access_token)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
command 'promote site' do |c|
|
2
|
+
c.syntax = 'skypager promote site'
|
3
|
+
c.description = 'Promotes this current folder to be the main folder for this site in the build server'
|
4
|
+
|
5
|
+
c.action do
|
6
|
+
app = Skypager.app
|
7
|
+
site = app.site
|
8
|
+
site.set(:root, app.root_path)
|
9
|
+
app.save_site_config
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
command 'list build queue' do |c|
|
14
|
+
c.syntax = "skypager list build queue"
|
15
|
+
c.description = "lists the sites in the build queue"
|
16
|
+
|
17
|
+
c.action do
|
18
|
+
Skypager::Site.requiring_build.each do |site|
|
19
|
+
puts "== site #{ site.name } at #{ site.root } requires a build"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -26,6 +26,9 @@ module Skypager
|
|
26
26
|
google_refresh_token: '',
|
27
27
|
google_access_token: '',
|
28
28
|
|
29
|
+
github_access_token: '',
|
30
|
+
github_repository: '',
|
31
|
+
|
29
32
|
domain: "skypager.io",
|
30
33
|
|
31
34
|
sites_directory: { }
|
@@ -47,8 +50,14 @@ module Skypager
|
|
47
50
|
super
|
48
51
|
end
|
49
52
|
|
50
|
-
def initialize!
|
53
|
+
def initialize!(fresh=false)
|
54
|
+
return if home_config_path.exist? && !fresh
|
55
|
+
|
51
56
|
FileUtils.mkdir_p home_config_path.dirname
|
57
|
+
|
58
|
+
home_config_path.open("w+") do |fh|
|
59
|
+
fh.write(DefaultSettings.to_json)
|
60
|
+
end
|
52
61
|
end
|
53
62
|
|
54
63
|
def dnsimple_setup?
|
@@ -98,13 +107,21 @@ module Skypager
|
|
98
107
|
save! if persist == true
|
99
108
|
end
|
100
109
|
|
110
|
+
def defaults
|
111
|
+
DefaultSettings.dup
|
112
|
+
end
|
113
|
+
|
101
114
|
def current
|
102
115
|
@current ||= begin
|
103
|
-
defaults
|
104
|
-
Hashie::Mash.new(defaults.merge(home_config.merge(cwd_config).merge(applied_config)))
|
116
|
+
defaults.merge(home_config.merge(cwd_config.merge(applied_config))).to_mash
|
105
117
|
end
|
106
118
|
end
|
107
119
|
|
120
|
+
def apply_config(hash={})
|
121
|
+
applied_config.merge!(hash)
|
122
|
+
current.merge(applied_config)
|
123
|
+
end
|
124
|
+
|
108
125
|
def apply_config_from_path(path)
|
109
126
|
path = Pathname(path)
|
110
127
|
parsed = JSON.parse(path.read) rescue {}
|
@@ -115,6 +132,8 @@ module Skypager
|
|
115
132
|
def save!
|
116
133
|
save_home_config
|
117
134
|
save_cwd_config
|
135
|
+
@current = nil
|
136
|
+
true
|
118
137
|
end
|
119
138
|
|
120
139
|
def save_cwd_config
|
@@ -146,6 +165,8 @@ module Skypager
|
|
146
165
|
end
|
147
166
|
|
148
167
|
def home_config
|
168
|
+
initialize! unless home_config_path.exist?
|
169
|
+
|
149
170
|
@home_config ||= begin
|
150
171
|
(home_config_path.exist? rescue false) ? JSON.parse(home_config_path.read) : {}
|
151
172
|
rescue
|
@@ -153,16 +174,20 @@ module Skypager
|
|
153
174
|
end
|
154
175
|
end
|
155
176
|
|
156
|
-
def
|
157
|
-
Pathname(
|
177
|
+
def home_config_path= value
|
178
|
+
@home_config_path = Pathname(value)
|
158
179
|
end
|
159
180
|
|
160
181
|
def home_config_path
|
161
|
-
|
182
|
+
@home_config_path || Pathname(ENV['HOME']).join(".skypager","config.json")
|
183
|
+
end
|
184
|
+
|
185
|
+
def cwd_config_path= value
|
186
|
+
@cwd_config_path = Pathname(value)
|
162
187
|
end
|
163
188
|
|
164
189
|
def cwd_config_path
|
165
|
-
Pathname(Dir.pwd).join('skypager.json')
|
190
|
+
@cwd_config_path || Pathname(Dir.pwd).join('skypager.json')
|
166
191
|
end
|
167
192
|
end
|
168
193
|
end
|
data/lib/skypager/core_ext.rb
CHANGED
@@ -150,7 +150,7 @@ module Skypager
|
|
150
150
|
end
|
151
151
|
|
152
152
|
def last_updated_at
|
153
|
-
if value = spreadsheet.
|
153
|
+
if value = spreadsheet.document_feed_entry_internal.css('updated').try(:text) rescue nil
|
154
154
|
DateTime.parse(value).to_i
|
155
155
|
end
|
156
156
|
end
|
@@ -166,7 +166,7 @@ module Skypager
|
|
166
166
|
protected
|
167
167
|
|
168
168
|
def process_worksheets
|
169
|
-
worksheets.inject(
|
169
|
+
worksheets.inject({}.to_mash) do |memo, parts|
|
170
170
|
k, ws = parts
|
171
171
|
header_row = Array(ws.rows[0])
|
172
172
|
column_names = header_row.map {|cell| "#{ cell }".parameterize.underscore }
|
@@ -205,7 +205,7 @@ module Skypager
|
|
205
205
|
end
|
206
206
|
|
207
207
|
def worksheets
|
208
|
-
@worksheets ||= _worksheets.inject(
|
208
|
+
@worksheets ||= _worksheets.inject({}.to_mash) do |memo,ws|
|
209
209
|
key = ws.title.strip.downcase.underscore.gsub(/\s+/,'_')
|
210
210
|
memo[key] = ws
|
211
211
|
memo
|
data/lib/skypager/data/source.rb
CHANGED
@@ -57,6 +57,24 @@ module Skypager
|
|
57
57
|
|
58
58
|
row
|
59
59
|
end
|
60
|
+
|
61
|
+
processors.each do |processor|
|
62
|
+
original = self.processed.dup
|
63
|
+
modified = []
|
64
|
+
|
65
|
+
original.each_with_index do |record, index|
|
66
|
+
previous = original[index - 1]
|
67
|
+
modified.push(processor.call(record, index, previous: previous, set: original))
|
68
|
+
end
|
69
|
+
|
70
|
+
self.processed = modified
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def processors &block
|
75
|
+
@processors ||= []
|
76
|
+
@processors << block if block_given?
|
77
|
+
@processors
|
60
78
|
end
|
61
79
|
|
62
80
|
# makes sure that the required options for this data source
|
@@ -94,6 +112,10 @@ module Skypager
|
|
94
112
|
!need_to_refresh? && (age > max_age)
|
95
113
|
end
|
96
114
|
|
115
|
+
def fresh_on_server?
|
116
|
+
need_to_refresh?
|
117
|
+
end
|
118
|
+
|
97
119
|
def max_age
|
98
120
|
max = ENV['MAX_DATA_SOURCE_AGE']
|
99
121
|
(max && max.to_i) || 120
|
@@ -139,7 +161,7 @@ module Skypager
|
|
139
161
|
return @refreshed_at if @refreshed_at.to_i > 0
|
140
162
|
|
141
163
|
if path_to_file.exist?
|
142
|
-
@refreshed_at =
|
164
|
+
@refreshed_at = File.mtime(path.join(file)).to_i
|
143
165
|
end
|
144
166
|
end
|
145
167
|
|
@@ -157,6 +179,7 @@ module Skypager
|
|
157
179
|
|
158
180
|
def file
|
159
181
|
@file ||= name.parameterize if name.respond_to?(:parameterize)
|
182
|
+
@file.gsub!("-","_")
|
160
183
|
@file = "#{@file}.json" unless @file.match(/\.json/i)
|
161
184
|
@file
|
162
185
|
end
|
data/lib/skypager/extension.rb
CHANGED
@@ -7,6 +7,7 @@ module Skypager
|
|
7
7
|
option :synced_folders, {}, 'The settings for dropbox source syncing'
|
8
8
|
option :data_sources, [], 'The data source mappings for this site'
|
9
9
|
option :deploy_options, {}, 'Deploy options: domain, bucket_name, use_cdn, aliases, auto_deploy'
|
10
|
+
option :skypager, {}, 'Options to be passed to Skypager.config'
|
10
11
|
|
11
12
|
def initialize(app, options_hash={}, &block)
|
12
13
|
# if only skypager/extension is required this will be necessary
|
@@ -16,27 +17,20 @@ module Skypager
|
|
16
17
|
|
17
18
|
app.include(ClassMethods)
|
18
19
|
|
19
|
-
options_hash.reverse_merge!(:data_sources => {},
|
20
|
-
:synced_folders => {},
|
21
|
-
:deploy_options => {},
|
20
|
+
options_hash.reverse_merge!(:data_sources => {}.to_mash,
|
21
|
+
:synced_folders => {}.to_mash,
|
22
|
+
:deploy_options => {}.to_mash,
|
22
23
|
:site_name => File.basename(app.root))
|
23
24
|
|
24
25
|
options_hash.each do |key, value|
|
25
26
|
app.set(key, value)
|
26
27
|
end
|
27
28
|
|
28
|
-
unless $skypager_command
|
29
|
+
unless $skypager_command || ENV['DISABLE_SKYPAGER_SYNC']
|
29
30
|
app.ready do
|
30
|
-
|
31
|
-
|
32
|
-
end
|
33
|
-
|
31
|
+
sync_folders()
|
32
|
+
load_stores()
|
34
33
|
# Load the data source and save it to disk
|
35
|
-
data_sources.each do |name, source|
|
36
|
-
source.refresh
|
37
|
-
source.save_to_disk unless source.persisted?
|
38
|
-
data.callbacks(name, -> { source.data })
|
39
|
-
end
|
40
34
|
end
|
41
35
|
|
42
36
|
# In development, we will try to refresh remote data stores (e.g. google spreadsheets)
|
@@ -46,19 +40,55 @@ module Skypager
|
|
46
40
|
data_source.refresh_if_stale?
|
47
41
|
end if development?
|
48
42
|
end
|
43
|
+
end
|
49
44
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
end
|
45
|
+
app.after_build do
|
46
|
+
app.site.requires_build!(false)
|
47
|
+
app.deploy! if app.auto_deploy?
|
48
|
+
end
|
55
49
|
|
50
|
+
app.after_configuration do
|
51
|
+
save_site_config()
|
56
52
|
end
|
53
|
+
|
57
54
|
end
|
58
55
|
|
59
56
|
helpers do
|
57
|
+
def deploy!
|
58
|
+
site.deploy()
|
59
|
+
end
|
60
|
+
|
61
|
+
def auto_deploy?
|
62
|
+
!!deploy_options[:auto_deploy]
|
63
|
+
end
|
64
|
+
|
65
|
+
def save_site_config
|
66
|
+
syncable_config = site.config.syncables ||= {}.to_mash
|
67
|
+
|
68
|
+
syncables.each do |folder|
|
69
|
+
type = folder.type
|
70
|
+
cfg = syncable_config[type] ||= {}.to_mash
|
71
|
+
cfg.paths ||= []
|
72
|
+
cfg.paths.push(syncable.remote_path.to_s)
|
73
|
+
cfg.paths.uniq!
|
74
|
+
end
|
75
|
+
|
76
|
+
if Skypager.config.dropbox_setup?
|
77
|
+
syncable_config.dropbox ||= {}
|
78
|
+
syncable_config.dropbox.uid = Skypager.dropbox.account.uid
|
79
|
+
end
|
80
|
+
|
81
|
+
site.set(:syncables, syncable_config)
|
82
|
+
end
|
83
|
+
|
60
84
|
def site
|
61
|
-
@site
|
85
|
+
return @site if @site
|
86
|
+
|
87
|
+
@site = Skypager::Site.new(site_name, deploy_options: deploy_options).tap do |site|
|
88
|
+
site.load_config
|
89
|
+
site.set(:deploy_options, deploy_options)
|
90
|
+
site.set(:root, root) unless site.config.key?(:root)
|
91
|
+
end
|
62
92
|
end
|
63
93
|
end
|
64
94
|
|
@@ -108,6 +138,39 @@ module Skypager
|
|
108
138
|
self.synced_folders[folder] = Skypager::Sync::Folder.new(f.merge(app: self, root:Pathname(root)))
|
109
139
|
|
110
140
|
save_dropbox_settings
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
def app
|
145
|
+
self
|
146
|
+
end
|
147
|
+
|
148
|
+
def stores
|
149
|
+
data_sources.values
|
150
|
+
end
|
151
|
+
|
152
|
+
def load_stores persist=false
|
153
|
+
data_sources.each do |name, source|
|
154
|
+
source.refresh
|
155
|
+
source.save_to_disk if persist || !source.persisted?
|
156
|
+
data.callbacks(name, -> { source.data })
|
157
|
+
end
|
158
|
+
|
159
|
+
after_stores_loaded.each do |callback|
|
160
|
+
callback = app.method(callback) if callback.is_a?(Symbol)
|
161
|
+
app.instance_eval(&callback) if callback.respond_to?(:call)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# middleman does have an event system, we can piggyback on that instead
|
166
|
+
def after_stores_loaded &block
|
167
|
+
@after_stores_callbacks ||= []
|
168
|
+
@after_stores_callbacks << block if block_given?
|
169
|
+
@after_stores_callbacks
|
170
|
+
end
|
171
|
+
|
172
|
+
def sync_folders
|
173
|
+
syncables.each(&:sync)
|
111
174
|
end
|
112
175
|
|
113
176
|
def syncables
|
@@ -129,9 +192,9 @@ module Skypager
|
|
129
192
|
end
|
130
193
|
|
131
194
|
@dropbox_data ||= begin
|
132
|
-
|
195
|
+
JSON.parse(dropbox_data_file.read).to_mash
|
133
196
|
rescue
|
134
|
-
|
197
|
+
{}.to_mash
|
135
198
|
end
|
136
199
|
end
|
137
200
|
|