microstatic 0.3.0 → 0.4.0

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.
data/README.md CHANGED
@@ -5,3 +5,6 @@ The microstatic gem turns generating your static site and deploying it to S3 int
5
5
  ## Credits
6
6
 
7
7
  The s3 deployment code was originally written by [Giles Alexander](http://twitter.com/gga)
8
+
9
+ ## TODO
10
+ - DONE! create Route 53 subdomain on demand
@@ -1,17 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'microstatic'
4
+ require 'microstatic/cli'
4
5
 
5
- if ARGV.empty?
6
- puts 'please specify a bucket name'
7
- exit 1
8
- end
6
+ require 'pry' if ENV.has_key? 'PRY_PLEASE'
9
7
 
10
- bucket_name = ARGV[0]
11
-
12
- bucket_creator = Microstatic::S3BucketCreator.new( Microstatic.aws_creds_from_env )
13
-
14
- puts "creating bucket #{bucket_name} ..."
15
- bucket_creator.create( bucket_name )
16
-
17
- puts "done"
8
+ Microstatic::CLI.start(ARGV)
@@ -1,9 +1,11 @@
1
1
  require "microstatic/version"
2
2
 
3
+ require 'microstatic/config'
3
4
  require 'microstatic/uses_fog'
4
5
 
5
6
  require "microstatic/s3_deployer"
6
7
  require "microstatic/s3_bucket_creator"
8
+ require "microstatic/route53_dns"
7
9
 
8
10
  module Microstatic
9
11
  def self.aws_creds_from_env
@@ -0,0 +1,72 @@
1
+ require 'thor'
2
+ require 'rainbow'
3
+
4
+ module Microstatic
5
+ class CLI < Thor
6
+ desc "setup <APP_NAME>", "do the needful to get started hosting your static site. S3, Route 53, whatever it takes."
7
+ def setup( app_name = false )
8
+ app_name ||= inferred_app_name
9
+ bucket_name = subdomain_for(app_name)
10
+
11
+ bucket = bucket( bucket_name )
12
+ # TODO: pass bucket through to dns setup so we don't have to look it up again
13
+ dns( app_name )
14
+
15
+ # TODO: add Rakefile, Gemfile, etc for doing a rake deploy using this gem
16
+ end
17
+
18
+ desc "bucket <BUCKET_NAME>", "create an S3 bucket which can host your static site"
19
+ def bucket( bucket_name = false )
20
+ bucket_name ||= guess_bucket_name
21
+ # TODO: check it doesn't already exist for you
22
+ # TODO: check bucket_name looks like a site name (e.g. foo.thepete.net, not just foo)
23
+ # TODO: handle the bucket name already being taken by someone else
24
+ # TODO: fail gracefully if aws creds not available
25
+
26
+ describe_operation( "create S3 bucket '#{bucket_name}'" ) do
27
+ S3BucketCreator.new( config.aws_creds ).create( bucket_name )
28
+ end
29
+ end
30
+
31
+ desc "dns <APP_NAME>", "create a Route 53 entry pointing to the S3 bucket containing your static site"
32
+ def dns( app_name = false )
33
+ app_name ||= inferred_app_name
34
+ bucket_name = subdomain_for(app_name)
35
+
36
+ describe_operation( "create Route 53 entry '#{bucket_name}'" ) do
37
+ Route53Dns.new( config.aws_creds ).add_s3_record_for_bucket( bucket_name )
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def guess_bucket_name
44
+ subdomain_for( inferred_app_name )
45
+ end
46
+
47
+ def subdomain_for( app_name )
48
+ "#{app_name}.#{config.site_name}"
49
+ end
50
+
51
+ def inferred_app_name
52
+ File.basename(Dir.pwd)
53
+ end
54
+
55
+ def config
56
+ Config.automagic
57
+ end
58
+
59
+ def describe_operation( operation_desc )
60
+ STDOUT.print " #{operation_desc} ..."
61
+ yield
62
+ STDOUT.puts "\r- #{operation_desc} "+CHECKMARK
63
+ rescue
64
+ STDOUT.puts "\r- #{operation_desc} "+SADMARK
65
+ raise
66
+ end
67
+
68
+ CHECKMARK="\u2713".color(:green)
69
+ SADMARK="\u2718".color(:red)
70
+
71
+ end
72
+ end
@@ -0,0 +1,28 @@
1
+ module Microstatic
2
+ class Config
3
+ def self.automagic
4
+ #in the future this'll try to create a config based on
5
+ #various diff. files, in priority order?
6
+ self.new
7
+ end
8
+
9
+ def site_name
10
+ ENV.fetch('MICROSTATIC_SITE_NAME')
11
+ end
12
+
13
+ def aws_creds
14
+ {
15
+ :access_key_id => aws_access_key_id,
16
+ :secret_access_key => aws_secret_access_key
17
+ }
18
+ end
19
+
20
+ def aws_access_key_id
21
+ ENV.fetch('AWS_ACCESS_KEY_ID')
22
+ end
23
+
24
+ def aws_secret_access_key
25
+ ENV.fetch('AWS_SECRET_ACCESS_KEY')
26
+ end
27
+ end
28
+ end
@@ -39,7 +39,7 @@ class S3DeployTask < ::Rake::TaskLib
39
39
  end
40
40
  end
41
41
 
42
- def self.s3_deploy_task(opts)
42
+ def self.s3_deploy_task(opts = {})
43
43
  task = S3DeployTask.new( opts )
44
44
  yield task if block_given?
45
45
  task.define
@@ -0,0 +1,42 @@
1
+ module Microstatic
2
+
3
+ class Route53Dns
4
+ include UsesFog
5
+
6
+ def initialize( aws_creds )
7
+ check_and_store_aws_creds(aws_creds)
8
+ end
9
+
10
+ def add_s3_record_for_bucket( bucket_name, hostname = false )
11
+ subdomain ||= bucket_name
12
+
13
+ zone = parent_zone_for_subdomain( subdomain )
14
+ cname_value = website_endpoint_for_bucket_named( bucket_name )
15
+
16
+ record = zone.records.create(
17
+ :name => subdomain,
18
+ :value => cname_value,
19
+ :type => 'CNAME',
20
+ :ttl => 86400
21
+ )
22
+ record
23
+ end
24
+
25
+ private
26
+
27
+ def website_endpoint_for_bucket_named( bucket_name )
28
+ bucket = connection.directories.get(bucket_name)
29
+
30
+ # per http://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteEndpoints.html
31
+ #TODO: get endpoint from API, or get this logic merged into fog
32
+ bucket_website_endpoint = "#{bucket.key}.s3-website-#{bucket.location}.amazonaws.com"
33
+ end
34
+
35
+ def parent_zone_for_subdomain( subdomain )
36
+ zone_name = subdomain.split(".")[1..-1].push("").join(".")
37
+ dns.zones.find{ |x| x.domain == zone_name }
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -8,9 +8,12 @@ class S3BucketCreator
8
8
  end
9
9
 
10
10
  def create( bucket_name )
11
- connection.put_bucket( bucket_name )
12
- connection.put_bucket_acl( bucket_name, 'public-read' )
13
- connection.put_bucket_website( bucket_name, 'index.html', :key => '404.html' )
11
+ # TODO: can we create a new directory without eagerly fetch the list of all dirs?
12
+ directory = connection.directories.create( :key => bucket_name, :public => true )
13
+ # TODO: can I do this by calling a method on directory?
14
+ connection.put_bucket_website( directory.key, 'index.html', :key => '404.html' )
15
+
16
+ directory
14
17
  end
15
18
  end
16
19
 
@@ -31,7 +31,7 @@ class S3Deployer
31
31
 
32
32
  if !s3_object
33
33
  log_action('CREATE', s3_key)
34
- connection.put_object( @bucket, s3_key, file.open, 'x-amz-acl' => 'public-read' )
34
+ put_file( s3_key, file )
35
35
  else
36
36
  s3_md5 = s3_object.headers['ETag'].sub(/"(.*)"/,'\1')
37
37
  local_md5 = Digest::MD5.hexdigest( file.read )
@@ -40,11 +40,17 @@ class S3Deployer
40
40
  log_action('NO CHANGE', s3_key)
41
41
  else
42
42
  log_action('UPDATE', s3_key)
43
- connection.put_object( @bucket, s3_key, file.open )
43
+ put_file( s3_key, file )
44
44
  end
45
45
  end
46
46
  end
47
47
 
48
+ private
49
+
50
+ def put_file( s3_key, file )
51
+ connection.put_object( @bucket, s3_key, file.open, 'x-amz-acl' => 'public-read', 'x-amz-storage-class' => 'REDUCED_REDUNDANCY' )
52
+ end
53
+
48
54
  def log_action(action,file)
49
55
  message = action.to_s.rjust(10) + " " + file
50
56
  puts message
@@ -10,8 +10,18 @@ module Microstatic
10
10
  @aws_creds = aws_creds
11
11
  end
12
12
 
13
+ # TODO: rename connection to storage
13
14
  def connection
14
15
  @_connection ||= Fog::Storage.new({
16
+ :provider => 'AWS',
17
+ :aws_access_key_id => @aws_creds.fetch(:access_key_id),
18
+ :aws_secret_access_key => @aws_creds.fetch(:secret_access_key),
19
+ :path_style => true
20
+ })
21
+ end
22
+
23
+ def dns
24
+ @_dns ||= Fog::DNS.new({
15
25
  :provider => 'AWS',
16
26
  :aws_access_key_id => @aws_creds.fetch(:access_key_id),
17
27
  :aws_secret_access_key => @aws_creds.fetch(:secret_access_key)
@@ -1,3 +1,3 @@
1
1
  module Microstatic
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -16,6 +16,8 @@ Gem::Specification.new do |gem|
16
16
  gem.version = Microstatic::VERSION
17
17
 
18
18
  gem.add_runtime_dependency "fog", ">=1"
19
+ gem.add_runtime_dependency "thor", ">=0.15" # 0.15 is an arbitrary guess at what might be API-compatible
20
+ gem.add_runtime_dependency "rainbow", ">= 1.1"
19
21
 
20
22
  gem.add_development_dependency "rake"
21
23
  gem.add_development_dependency "bundler"
@@ -1,3 +1,10 @@
1
1
  require_relative '../../lib/microstatic'
2
+ # require_relative '../../lib/microstatic/uses_fog'
2
3
 
4
+ if ENV['FOG_MOCK']
5
+ Fog.mock!
6
+ include Microstatic::UsesFog
7
+ check_and_store_aws_creds(Microstatic.aws_creds_from_env)
8
+ connection.directories.create(:key => 'microstatic-test-bucket')
9
+ end
3
10
  require 'pry'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: microstatic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-02 00:00:00.000000000 Z
12
+ date: 2013-12-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fog
@@ -27,6 +27,38 @@ dependencies:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: '1'
30
+ - !ruby/object:Gem::Dependency
31
+ name: thor
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0.15'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0.15'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rainbow
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '1.1'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '1.1'
30
62
  - !ruby/object:Gem::Dependency
31
63
  name: rake
32
64
  requirement: !ruby/object:Gem::Requirement
@@ -109,7 +141,10 @@ files:
109
141
  - Rakefile
110
142
  - bin/microstatic
111
143
  - lib/microstatic.rb
144
+ - lib/microstatic/cli.rb
145
+ - lib/microstatic/config.rb
112
146
  - lib/microstatic/rake.rb
147
+ - lib/microstatic/route53_dns.rb
113
148
  - lib/microstatic/s3_bucket_creator.rb
114
149
  - lib/microstatic/s3_deployer.rb
115
150
  - lib/microstatic/uses_fog.rb
@@ -135,7 +170,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
135
170
  version: '0'
136
171
  segments:
137
172
  - 0
138
- hash: 2953068700068104113
173
+ hash: 3071074031925254256
139
174
  required_rubygems_version: !ruby/object:Gem::Requirement
140
175
  none: false
141
176
  requirements:
@@ -144,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
144
179
  version: '0'
145
180
  segments:
146
181
  - 0
147
- hash: 2953068700068104113
182
+ hash: 3071074031925254256
148
183
  requirements: []
149
184
  rubyforge_project:
150
185
  rubygems_version: 1.8.25