strobe 0.0.2 → 0.0.2.1

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.
@@ -1,6 +1,7 @@
1
1
  require 'base64'
2
2
  require 'active_model'
3
3
  require 'active_support/concern'
4
+ require 'active_support/json'
4
5
  require 'active_support/core_ext/hash/deep_merge'
5
6
  require 'active_support/core_ext/hash/reverse_merge'
6
7
  require 'active_support/core_ext/hash/indifferent_access'
@@ -40,7 +40,8 @@ module Strobe
40
40
  method_option "path", :type => :string, :banner => "The path to the application"
41
41
  def deploy
42
42
  path = options[:path] || Dir.pwd
43
- config = Settings.new(Pathname.new(path).join(".strobe/config"))
43
+ file = ENV['STROBE_CONFIG'] || 'config'
44
+ config = Settings.new(Pathname.new(path).join(".strobe/#{file}"))
44
45
 
45
46
  _run :deploy, :config => config, :path => path
46
47
  end
@@ -49,8 +49,7 @@ module Strobe
49
49
 
50
50
  class DeployAction < CLI::Action
51
51
  def steps
52
- ensure_computer_is_registered
53
-
52
+ ensure_computer_is_registered
54
53
 
55
54
  config = options[:config]
56
55
  path = determine_path
@@ -68,12 +67,14 @@ module Strobe
68
67
 
69
68
  ask :name
70
69
  ask :url
71
- end
72
70
 
73
- save do
74
- config[:application_id] ||= resource[:id]
75
- say "The application has successfully been deployed and is available at #{resource[:url]}"
71
+ save do
72
+ config[:application_id] ||= resource[:id]
73
+ end
76
74
  end
75
+
76
+ resource.deploy!
77
+ say "The application has successfully been deployed and is available at #{resource[:url]}"
77
78
  end
78
79
 
79
80
  private
@@ -3,11 +3,13 @@ require 'yaml'
3
3
  module Strobe
4
4
  class CLI::Settings
5
5
  def self.global_config_file
6
- Pathname.new(Strobe.user_home).join(".strobe/config")
6
+ file = ENV['STROBE_CONFIG'] || 'config'
7
+ Pathname.new(Strobe.user_home).join(".strobe/#{file}")
7
8
  end
8
9
 
9
10
  def self.application_config_file(root)
10
- Pathname.new(root).join(".strobe/config")
11
+ file = ENV['STROBE_CONFIG'] || 'config'
12
+ Pathname.new(root).join(".strobe/#{file}")
11
13
  end
12
14
 
13
15
  def initialize(filename = self.class.global_config_file)
@@ -1,12 +1,12 @@
1
1
  require 'base64'
2
- require 'faraday'
2
+ require 'net/http'
3
3
 
4
4
  module Strobe
5
5
  class Connection
6
6
  attr_reader :url
7
7
 
8
8
  def initialize(url)
9
- @url = url
9
+ @url = URI(url)
10
10
  @user = nil
11
11
  @password = nil
12
12
  end
@@ -37,27 +37,100 @@ module Strobe
37
37
  request :delete, *args
38
38
  end
39
39
 
40
- def request(method, path, body = '', headers = {})
41
- connection = case headers['Content-Type']
42
- when /^multipart\/form-data;/ then reg_connection
43
- else json_connection
40
+ class Response
41
+ attr_reader :headers
42
+
43
+ def initialize(response)
44
+ @response = response
45
+ @headers = {}
46
+
47
+ response.each_header do |key, val|
48
+ @headers[key] = val
49
+ end
50
+ end
51
+
52
+ def status
53
+ @response.code.to_i
54
+ end
55
+
56
+ def body
57
+ return @body if @body
58
+
59
+ case mime_type
60
+ when :json
61
+ @body = ActiveSupport::JSON.decode(@response.body)
62
+ else
63
+ @body = @response.body
64
+ end
65
+
66
+ @body
67
+ end
68
+
69
+ def success?
70
+ status == 200
71
+ end
72
+
73
+ def validate!
74
+ case status
75
+ when 401
76
+ # TODO Fix this on the server
77
+ raise UnauthenticatedError, body['errors']
78
+ when 404
79
+ raise ResourceNotFoundError, body['errors']
80
+ end
81
+
82
+ self
44
83
  end
45
84
 
46
- resp = connection.run_request method, path, body, headers
85
+ private
47
86
 
48
- case resp.status
49
- when 401
50
- # TODO Fix this on the server
51
- raise UnauthenticatedError, resp.body['errors']
52
- when 404
53
- raise ResourceNotFoundError, resp.body['errors']
87
+ def mime_type
88
+ return nil unless content_type = headers['content-type']
89
+
90
+ if content_type =~ /^application\/json[,\;\s]/
91
+ :json
92
+ end
93
+ end
94
+ end
95
+
96
+ def request(method, path, body = nil, headers = {})
97
+ headers.keys.each do |key|
98
+ headers[key.downcase] = headers.delete(key)
54
99
  end
55
100
 
56
- resp
101
+ headers['authorization'] ||= authorization_header
102
+
103
+ if body
104
+ headers['content-type'] ||= 'application/json'
105
+
106
+ if headers['content-type'] == 'application/json'
107
+ body = ActiveSupport::JSON.encode(body)
108
+ else
109
+ body = body.to_s
110
+ end
111
+ end
112
+
113
+ if body.respond_to?(:read)
114
+ request.body_stream = body
115
+ body = nil
116
+ end
117
+
118
+ http = build_http
119
+ request = Net::HTTPGenericRequest.new(
120
+ method.to_s.upcase, !!body, true, path, headers)
121
+
122
+ Response.new(http.request(request, body)).validate!
57
123
  end
58
124
 
59
125
  private
60
126
 
127
+ def build_http
128
+ http = Net::HTTP.new @url.host, @url.port || 80
129
+ http.read_timeout = 60
130
+ http.open_timeout = 10
131
+ http
132
+ end
133
+
61
134
  def authorization_header
62
135
  auth = Base64.encode64("#{@user}:#{@password}")
63
136
  auth.gsub!("\n", "")
@@ -74,7 +147,6 @@ module Strobe
74
147
  b.use Faraday::Request::ActiveSupportJson
75
148
  b.use Faraday::Adapter::NetHttp
76
149
  b.use Faraday::Response::ActiveSupportJson
77
- b.options[:timeout] = 30
78
150
  b.headers['authorization'] = authorization_header if authorize?
79
151
  end
80
152
  end
@@ -83,7 +155,6 @@ module Strobe
83
155
  @reg_connection ||= Faraday::Connection.new(:url => @url) do |b|
84
156
  b.use Faraday::Adapter::NetHttp
85
157
  b.use Faraday::Response::ActiveSupportJson
86
- b.options[:timeout] = 30
87
158
  b.headers['authorization'] = authorization_header if authorize?
88
159
  end
89
160
  end
@@ -146,21 +146,7 @@ module Strobe
146
146
 
147
147
  def save
148
148
  if valid?
149
- root = self.class.singular_resource_name
150
- @response = self["id"] ? update : create
151
-
152
- if @response.success?
153
- merge! @response.body[root]
154
- return true
155
- elsif @response.status == 422
156
- errors = @response.body['errors']
157
- errors = errors[root] if errors
158
- handle_errors(errors) if errors
159
- else
160
- raise "Something went wrong"
161
- end
162
-
163
- false
149
+ request { self["id"] ? update : create }
164
150
  end
165
151
  end
166
152
 
@@ -184,6 +170,24 @@ module Strobe
184
170
  uri
185
171
  end
186
172
 
173
+ def request
174
+ @response = yield
175
+ root = self.class.singular_resource_name
176
+
177
+ if @response.success?
178
+ merge! @response.body[root]
179
+ return true
180
+ elsif @response.status == 422
181
+ errors = @response.body['errors']
182
+ errors = errors[root] if errors
183
+ handle_errors(errors) if errors
184
+ else
185
+ raise "Something went wrong"
186
+ end
187
+
188
+ false
189
+ end
190
+
187
191
  def request_params
188
192
  filter = self.class.filter
189
193
  { self.class.singular_resource_name => params.except(*filter) }
@@ -1,3 +1,4 @@
1
+ require 'digest/sha1'
1
2
  require 'mime/types'
2
3
 
3
4
  module Strobe
@@ -12,37 +13,45 @@ module Strobe
12
13
  filter :path
13
14
 
14
15
  validates "account", "name", "url", :presence => true
15
- validates "path", :presence => true, :with => :ensure_sane_application_path,
16
- :skippable => false
17
16
 
18
- private
17
+ def deploy!(path = nil)
18
+ self['path'] = path if path
19
19
 
20
- def ensure_sane_application_path
21
- if self[:path] && Dir["#{self[:path]}/**/*"].empty?
22
- errors.add('path', 'is not a sane application directory')
20
+ validate_for_deploy or return
21
+
22
+ request do
23
+ packfile = build_packfile
24
+ connection.put("#{http_uri}/deploy", packfile.to_s, packfile.headers)
23
25
  end
24
26
  end
25
27
 
26
- def create
27
- multipart = build_multipart_request
28
- connection.post http_uri, multipart.body, multipart.headers
28
+ private
29
+
30
+ def validate_for_deploy
31
+ errors.clear
32
+
33
+ validates_presence_of :path
34
+ ensure_sane_application_path
35
+
36
+ errors.empty?
29
37
  end
30
38
 
31
- def update
32
- multipart = build_multipart_request
33
- connection.put http_uri, multipart.body, multipart.headers
39
+ def ensure_sane_application_path
40
+ if self[:path] && Dir["#{self[:path]}/**/*"].empty?
41
+ errors.add('path', 'is not a sane application directory')
42
+ end
34
43
  end
35
44
 
36
- def build_multipart_request
45
+ def build_packfile
37
46
  Dir.chdir self[:path] do
38
- Multipart.new request_params do |m|
47
+ PackFile.build do |m|
39
48
  Dir["**/*"].each do |filename|
40
49
  next unless File.file?(filename)
41
50
 
42
51
  mime_type = MIME::Types.type_for(filename).first
43
52
  mime_type ||= "application/octet-stream"
44
53
 
45
- m.file filename, mime_type.to_s, File.read(filename)
54
+ m.file filename, mime_type.to_s, read(filename)
46
55
  end
47
56
 
48
57
  picked_root_app = false
@@ -56,8 +65,8 @@ module Strobe
56
65
  next unless index && File.file?(index)
57
66
 
58
67
  manifest = "#{File.dirname(index)}/app.manifest"
59
- manifest = File.read(manifest) if File.file?(manifest)
60
- content = File.read(index)
68
+ manifest = read(manifest) if File.file?(manifest)
69
+ content = read(index)
61
70
 
62
71
  m.file "#{app_name}/index.html", "text/html", content
63
72
  m.file "#{app_name}/app.manifest", "text/cache-manifest", manifest if manifest
@@ -73,14 +82,19 @@ module Strobe
73
82
  end
74
83
  end
75
84
 
76
- class Multipart
77
- attr_reader :params
85
+ def read(path)
86
+ File.open(path, 'rb') { |f| f.read }
87
+ end
78
88
 
79
- def initialize(params)
80
- @params = params
81
- @files = []
89
+ class PackFile
90
+ def self.build
91
+ inst = new
92
+ yield inst
93
+ inst
94
+ end
82
95
 
83
- yield self if block_given?
96
+ def initialize
97
+ @files = []
84
98
  end
85
99
 
86
100
  def file(path, content_type, body)
@@ -88,40 +102,20 @@ module Strobe
88
102
  end
89
103
 
90
104
  def headers
91
- { "Content-Type" => %[multipart/form-data; boundary="#{boundary}"] }
105
+ { 'Content-Type' => 'application/x-strobe-deploy' }
92
106
  end
93
107
 
94
- def body
95
- b = "--#{boundary}\r\n"
96
- b << part("metadata", "application/json", metadata)
97
- b << "--#{boundary}\r\n"
108
+ def to_s
109
+ head = ""
110
+ body = ""
98
111
 
99
- @files.each do |path, type, body|
100
- b << part("files[]", type, body)
101
- b << "--#{boundary}\r\n"
112
+ @files.each do |path, type, data|
113
+ size = data.respond_to?(:bytesize) ? data.bytesize : data.size
114
+ head << "#{Digest::SHA1.hexdigest(data)} #{size} #{path} #{type}\n"
115
+ body << data
102
116
  end
103
117
 
104
- b
105
- end
106
-
107
- private
108
-
109
- def boundary
110
- @boundary ||= ActiveSupport::SecureRandom.hex(25)
111
- end
112
-
113
- def metadata
114
- params['application']['paths'] = @files.map { |parts| parts.first }
115
- params.to_json
116
- end
117
-
118
- def part(name, type, body)
119
- b = ""
120
- b << %[Content-Disposition: form-data; name="#{name}"; filename="array"\r\n]
121
- b << %[Content-Type: #{type}\r\n]
122
- b << %[\r\n]
123
- b << body
124
- b << %[\r\n]
118
+ "#{head}\n#{body}"
125
119
  end
126
120
  end
127
121
  end
metadata CHANGED
@@ -6,7 +6,8 @@ version: !ruby/object:Gem::Version
6
6
  - 0
7
7
  - 0
8
8
  - 2
9
- version: 0.0.2
9
+ - 1
10
+ version: 0.0.2.1
10
11
  platform: ruby
11
12
  authors:
12
13
  - Yehuda Katz
@@ -15,7 +16,7 @@ autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
18
 
18
- date: 2010-12-14 00:00:00 -08:00
19
+ date: 2010-12-15 00:00:00 -08:00
19
20
  default_executable:
20
21
  dependencies:
21
22
  - !ruby/object:Gem::Dependency
@@ -48,25 +49,10 @@ dependencies:
48
49
  version: 3.0.0
49
50
  type: :runtime
50
51
  version_requirements: *id002
51
- - !ruby/object:Gem::Dependency
52
- name: faraday
53
- prerelease: false
54
- requirement: &id003 !ruby/object:Gem::Requirement
55
- none: false
56
- requirements:
57
- - - ~>
58
- - !ruby/object:Gem::Version
59
- segments:
60
- - 0
61
- - 5
62
- - 0
63
- version: 0.5.0
64
- type: :runtime
65
- version_requirements: *id003
66
52
  - !ruby/object:Gem::Dependency
67
53
  name: highline
68
54
  prerelease: false
69
- requirement: &id004 !ruby/object:Gem::Requirement
55
+ requirement: &id003 !ruby/object:Gem::Requirement
70
56
  none: false
71
57
  requirements:
72
58
  - - ~>
@@ -77,11 +63,11 @@ dependencies:
77
63
  - 0
78
64
  version: 1.6.0
79
65
  type: :runtime
80
- version_requirements: *id004
66
+ version_requirements: *id003
81
67
  - !ruby/object:Gem::Dependency
82
68
  name: thor
83
69
  prerelease: false
84
- requirement: &id005 !ruby/object:Gem::Requirement
70
+ requirement: &id004 !ruby/object:Gem::Requirement
85
71
  none: false
86
72
  requirements:
87
73
  - - ~>
@@ -92,11 +78,11 @@ dependencies:
92
78
  - 0
93
79
  version: 0.14.0
94
80
  type: :runtime
95
- version_requirements: *id005
81
+ version_requirements: *id004
96
82
  - !ruby/object:Gem::Dependency
97
83
  name: mime-types
98
84
  prerelease: false
99
- requirement: &id006 !ruby/object:Gem::Requirement
85
+ requirement: &id005 !ruby/object:Gem::Requirement
100
86
  none: false
101
87
  requirements:
102
88
  - - ~>
@@ -107,7 +93,7 @@ dependencies:
107
93
  - 0
108
94
  version: 1.16.0
109
95
  type: :runtime
110
- version_requirements: *id006
96
+ version_requirements: *id005
111
97
  description: The client library for deploying applications to Strobe's HTML5 deployment platform
112
98
  email:
113
99
  - wycats@strobecorp.com