s3_website 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.travis.yml +5 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +81 -0
- data/LICENSE +42 -0
- data/README.md +332 -0
- data/Rakefile +18 -0
- data/bin/s3_website +38 -0
- data/changelog.md +7 -0
- data/example-configurations.md +60 -0
- data/features/as-library.feature +29 -0
- data/features/cassettes/cucumber_tags/create-redirect.yml +384 -0
- data/features/cassettes/cucumber_tags/new-and-changed-files.yml +303 -0
- data/features/cassettes/cucumber_tags/new-files-for-sydney.yml +211 -0
- data/features/cassettes/cucumber_tags/new-files.yml +355 -0
- data/features/cassettes/cucumber_tags/no-new-or-changed-files.yml +359 -0
- data/features/cassettes/cucumber_tags/one-file-to-delete.yml +390 -0
- data/features/cassettes/cucumber_tags/only-changed-files.yml +411 -0
- data/features/cassettes/cucumber_tags/s3-and-cloudfront-when-updating-a-file.yml +435 -0
- data/features/cassettes/cucumber_tags/s3-and-cloudfront.yml +290 -0
- data/features/cloudfront.feature +35 -0
- data/features/delete.feature +18 -0
- data/features/instructions-for-new-user.feature +94 -0
- data/features/redirects.feature +16 -0
- data/features/step_definitions/steps.rb +67 -0
- data/features/support/env.rb +26 -0
- data/features/support/test_site_dirs/cdn-powered.blog.fi/_site/css/styles.css +3 -0
- data/features/support/test_site_dirs/cdn-powered.blog.fi/_site/index.html +5 -0
- data/features/support/test_site_dirs/cdn-powered.blog.fi/s3_website.yml +4 -0
- data/features/support/test_site_dirs/cdn-powered.with-one-change.blog.fi/_site/css/styles.css +3 -0
- data/features/support/test_site_dirs/cdn-powered.with-one-change.blog.fi/_site/index.html +10 -0
- data/features/support/test_site_dirs/cdn-powered.with-one-change.blog.fi/s3_website.yml +4 -0
- data/features/support/test_site_dirs/create-redirects/_site/.gitkeep +0 -0
- data/features/support/test_site_dirs/create-redirects/s3_website.yml +6 -0
- data/features/support/test_site_dirs/index-and-assets.blog.fi/_site/assets/picture.gif +0 -0
- data/features/support/test_site_dirs/index-and-assets.blog.fi/_site/css/styles.css +3 -0
- data/features/support/test_site_dirs/index-and-assets.blog.fi/_site/index.html +5 -0
- data/features/support/test_site_dirs/index-and-assets.blog.fi/s3_website.yml +3 -0
- data/features/support/test_site_dirs/my.blog.com/_site/css/styles.css +3 -0
- data/features/support/test_site_dirs/my.blog.com/_site/index.html +5 -0
- data/features/support/test_site_dirs/my.blog.com/s3_website.yml +3 -0
- data/features/support/test_site_dirs/my.sydney.blog.au/_site/css/styles.css +3 -0
- data/features/support/test_site_dirs/my.sydney.blog.au/_site/index.html +5 -0
- data/features/support/test_site_dirs/my.sydney.blog.au/s3_website.yml +4 -0
- data/features/support/test_site_dirs/new-and-changed-files.com/_site/css/styles.css +4 -0
- data/features/support/test_site_dirs/new-and-changed-files.com/_site/index.html +8 -0
- data/features/support/test_site_dirs/new-and-changed-files.com/s3_website.yml +3 -0
- data/features/support/test_site_dirs/no-new-or-changed-files.com/_site/css/styles.css +3 -0
- data/features/support/test_site_dirs/no-new-or-changed-files.com/_site/index.html +5 -0
- data/features/support/test_site_dirs/no-new-or-changed-files.com/s3_website.yml +3 -0
- data/features/support/test_site_dirs/only-changed-files.com/_site/css/styles.css +3 -0
- data/features/support/test_site_dirs/only-changed-files.com/_site/index.html +9 -0
- data/features/support/test_site_dirs/only-changed-files.com/s3_website.yml +3 -0
- data/features/support/test_site_dirs/site.with.css-maxage.com/_site/css/styles.css +3 -0
- data/features/support/test_site_dirs/site.with.css-maxage.com/_site/index.html +5 -0
- data/features/support/test_site_dirs/site.with.css-maxage.com/s3_website.yml +5 -0
- data/features/support/test_site_dirs/site.with.gzipped-and-max-aged-content.com/_site/css/styles.css +3 -0
- data/features/support/test_site_dirs/site.with.gzipped-and-max-aged-content.com/_site/index.html +5 -0
- data/features/support/test_site_dirs/site.with.gzipped-and-max-aged-content.com/s3_website.yml +5 -0
- data/features/support/test_site_dirs/site.with.gzipped-html.com/_site/css/styles.css +3 -0
- data/features/support/test_site_dirs/site.with.gzipped-html.com/_site/index.html +5 -0
- data/features/support/test_site_dirs/site.with.gzipped-html.com/s3_website.yml +5 -0
- data/features/support/test_site_dirs/site.with.maxage.com/_site/css/styles.css +3 -0
- data/features/support/test_site_dirs/site.with.maxage.com/_site/index.html +5 -0
- data/features/support/test_site_dirs/site.with.maxage.com/s3_website.yml +4 -0
- data/features/support/test_site_dirs/unpublish-a-post.com/_site/css/styles.css +3 -0
- data/features/support/test_site_dirs/unpublish-a-post.com/s3_website.yml +3 -0
- data/features/support/vcr.rb +18 -0
- data/features/sync.feature +80 -0
- data/features/website-performance.feature +57 -0
- data/lib/cloudfront/invalidator.rb +22 -0
- data/lib/s3_website.rb +20 -0
- data/lib/s3_website/config_loader.rb +56 -0
- data/lib/s3_website/diff_helper.rb +21 -0
- data/lib/s3_website/endpoint.rb +30 -0
- data/lib/s3_website/errors.rb +28 -0
- data/lib/s3_website/keyboard.rb +27 -0
- data/lib/s3_website/parallelism.rb +18 -0
- data/lib/s3_website/retry.rb +19 -0
- data/lib/s3_website/tasks.rb +42 -0
- data/lib/s3_website/upload.rb +103 -0
- data/lib/s3_website/uploader.rb +160 -0
- data/s3-website.gemspec +39 -0
- data/spec/lib/config_loader_spec.rb +20 -0
- data/spec/lib/endpoint_spec.rb +27 -0
- data/spec/lib/keyboard_spec.rb +59 -0
- data/spec/lib/parallelism_spec.rb +43 -0
- data/spec/lib/retry_spec.rb +34 -0
- data/spec/lib/upload_spec.rb +205 -0
- data/spec/lib/uploader_spec.rb +30 -0
- data/spec/sample_files/hyde_site/_site/.vimrc +5 -0
- data/spec/sample_files/hyde_site/_site/css/styles.css +3 -0
- data/spec/sample_files/hyde_site/_site/index.html +1 -0
- data/spec/sample_files/hyde_site/s3_website.yml +3 -0
- data/spec/sample_files/tokyo_site/_site/.vimrc +5 -0
- data/spec/sample_files/tokyo_site/_site/css/styles.css +3 -0
- data/spec/sample_files/tokyo_site/_site/index.html +1 -0
- data/spec/sample_files/tokyo_site/s3_website.yml +4 -0
- data/spec/spec_helper.rb +1 -0
- metadata +416 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'vcr'
|
2
|
+
|
3
|
+
VCR.configure do |c|
|
4
|
+
c.hook_into :webmock
|
5
|
+
c.cassette_library_dir = 'features/cassettes'
|
6
|
+
end
|
7
|
+
|
8
|
+
VCR.cucumber_tags do |t|
|
9
|
+
t.tag '@new-files'
|
10
|
+
t.tag '@new-files-for-sydney'
|
11
|
+
t.tag '@new-and-changed-files'
|
12
|
+
t.tag '@only-changed-files'
|
13
|
+
t.tag '@no-new-or-changed-files'
|
14
|
+
t.tag '@s3-and-cloudfront'
|
15
|
+
t.tag '@s3-and-cloudfront-when-updating-a-file'
|
16
|
+
t.tag '@one-file-to-delete'
|
17
|
+
t.tag '@create-redirect'
|
18
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
Feature: upload S3 website to S3
|
2
|
+
|
3
|
+
In order to push my website to S3
|
4
|
+
As a blogger
|
5
|
+
I want to run s3_website and say OMG it just worked!
|
6
|
+
|
7
|
+
@new-files
|
8
|
+
Scenario: Push a new S3 website to S3
|
9
|
+
When my S3 website is in "features/support/test_site_dirs/my.blog.com"
|
10
|
+
Then s3_website will push my blog to S3
|
11
|
+
And the output should contain
|
12
|
+
"""
|
13
|
+
Deploying _site/* to s3-website-test.net
|
14
|
+
Uploading 2 new file(s)
|
15
|
+
"""
|
16
|
+
And the output should contain
|
17
|
+
"""
|
18
|
+
Upload css/styles.css: Success!
|
19
|
+
"""
|
20
|
+
And the output should contain
|
21
|
+
"""
|
22
|
+
Upload index.html: Success!
|
23
|
+
"""
|
24
|
+
|
25
|
+
@new-files-for-sydney
|
26
|
+
Scenario: Push a new S3 website to an S3 bucket in Sydney
|
27
|
+
When my S3 website is in "features/support/test_site_dirs/my.sydney.blog.au"
|
28
|
+
Then s3_website will push my blog to S3
|
29
|
+
And the output should contain
|
30
|
+
"""
|
31
|
+
Done! Go visit: http://s3-website-test.net.s3-website-ap-southeast-2.amazonaws.com/index.html
|
32
|
+
"""
|
33
|
+
|
34
|
+
@new-and-changed-files
|
35
|
+
Scenario: Upload a new blog post and change an old post
|
36
|
+
When my S3 website is in "features/support/test_site_dirs/new-and-changed-files.com"
|
37
|
+
Then s3_website will push my blog to S3
|
38
|
+
And the output should contain
|
39
|
+
"""
|
40
|
+
Deploying _site/* to s3-website-test.net
|
41
|
+
Uploading 1 new and 1 changed file(s)
|
42
|
+
"""
|
43
|
+
And the output should contain
|
44
|
+
"""
|
45
|
+
Upload css/styles.css: Success!
|
46
|
+
"""
|
47
|
+
And the output should contain
|
48
|
+
"""
|
49
|
+
Upload index.html: Success!
|
50
|
+
"""
|
51
|
+
And the output should contain
|
52
|
+
"""
|
53
|
+
Done! Go visit: http://s3-website-test.net.s3-website-us-east-1.amazonaws.com/index.html
|
54
|
+
|
55
|
+
"""
|
56
|
+
|
57
|
+
@only-changed-files
|
58
|
+
Scenario: Update an existing blog post
|
59
|
+
When my S3 website is in "features/support/test_site_dirs/only-changed-files.com"
|
60
|
+
Then s3_website will push my blog to S3
|
61
|
+
And the output should equal
|
62
|
+
"""
|
63
|
+
Deploying _site/* to s3-website-test.net
|
64
|
+
Uploading 1 changed file(s)
|
65
|
+
Upload index.html: Success!
|
66
|
+
Done! Go visit: http://s3-website-test.net.s3-website-us-east-1.amazonaws.com/index.html
|
67
|
+
|
68
|
+
"""
|
69
|
+
|
70
|
+
@no-new-or-changed-files
|
71
|
+
Scenario: The user runs s3_website even though he doesn't have new or changed posts
|
72
|
+
When my S3 website is in "features/support/test_site_dirs/no-new-or-changed-files.com"
|
73
|
+
Then s3_website will push my blog to S3
|
74
|
+
And the output should equal
|
75
|
+
"""
|
76
|
+
Deploying _site/* to s3-website-test.net
|
77
|
+
No new or changed files to upload
|
78
|
+
Done! Go visit: http://s3-website-test.net.s3-website-us-east-1.amazonaws.com/index.html
|
79
|
+
|
80
|
+
"""
|
@@ -0,0 +1,57 @@
|
|
1
|
+
Feature: improve response times of your S3 website website
|
2
|
+
|
3
|
+
As a blogger
|
4
|
+
I want to benefit from HTTP performance optimisations
|
5
|
+
So that my readers would not have to wait long for my website to load
|
6
|
+
|
7
|
+
@new-files
|
8
|
+
Scenario: Set Cache-Control: max-age for all uploaded files
|
9
|
+
When my S3 website is in "features/support/test_site_dirs/site.with.maxage.com"
|
10
|
+
Then s3_website will push my blog to S3
|
11
|
+
And the output should contain
|
12
|
+
"""
|
13
|
+
Upload css/styles.css [max-age=120]: Success!
|
14
|
+
"""
|
15
|
+
And the output should contain
|
16
|
+
"""
|
17
|
+
Upload index.html [max-age=120]: Success!
|
18
|
+
"""
|
19
|
+
|
20
|
+
@new-files
|
21
|
+
Scenario: Set Cache-Control: max-age for CSS files only
|
22
|
+
When my S3 website is in "features/support/test_site_dirs/site.with.css-maxage.com"
|
23
|
+
Then s3_website will push my blog to S3
|
24
|
+
And the output should contain
|
25
|
+
"""
|
26
|
+
Upload css/styles.css [max-age=100]: Success!
|
27
|
+
"""
|
28
|
+
And the output should contain
|
29
|
+
"""
|
30
|
+
Upload index.html [max-age=0]: Success!
|
31
|
+
"""
|
32
|
+
|
33
|
+
@new-files
|
34
|
+
Scenario: Set Content-Encoding: gzip HTTP header for HTML files
|
35
|
+
When my S3 website is in "features/support/test_site_dirs/site.with.gzipped-html.com"
|
36
|
+
Then s3_website will push my blog to S3
|
37
|
+
And the output should contain
|
38
|
+
"""
|
39
|
+
Upload css/styles.css: Success!
|
40
|
+
"""
|
41
|
+
And the output should contain
|
42
|
+
"""
|
43
|
+
Upload index.html [gzipped]: Success!
|
44
|
+
"""
|
45
|
+
|
46
|
+
@new-files
|
47
|
+
Scenario: Set both the Content-Encoding: gzip and Cache-Control: max-age headers
|
48
|
+
When my S3 website is in "features/support/test_site_dirs/site.with.gzipped-and-max-aged-content.com"
|
49
|
+
Then s3_website will push my blog to S3
|
50
|
+
And the output should contain
|
51
|
+
"""
|
52
|
+
Upload css/styles.css [gzipped] [max-age=300]: Success!
|
53
|
+
"""
|
54
|
+
And the output should contain
|
55
|
+
"""
|
56
|
+
Upload index.html [gzipped] [max-age=300]: Success!
|
57
|
+
"""
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module S3Website
|
2
|
+
module Cloudfront
|
3
|
+
class Invalidator
|
4
|
+
def self.invalidate(config, changed_files)
|
5
|
+
aws_key = config['s3_id']
|
6
|
+
aws_secret = config['s3_secret']
|
7
|
+
s3_bucket_name = config['s3_bucket']
|
8
|
+
cloudfront_distribution_id = config['cloudfront_distribution_id']
|
9
|
+
s3 = AWS::S3.new(
|
10
|
+
:access_key_id => aws_key,
|
11
|
+
:secret_access_key => aws_secret)
|
12
|
+
s3_object_keys = changed_files
|
13
|
+
s3_object_keys << ""
|
14
|
+
report = SimpleCloudfrontInvalidator::CloudfrontClient.new(
|
15
|
+
aws_key, aws_secret, cloudfront_distribution_id).invalidate(
|
16
|
+
s3_object_keys)
|
17
|
+
puts report[:text_report]
|
18
|
+
report[:invalidated_items_count]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/s3_website.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'yaml'
|
3
|
+
require 'erubis'
|
4
|
+
require 'aws-sdk'
|
5
|
+
require 'simple-cloudfront-invalidator'
|
6
|
+
require 'filey-diff'
|
7
|
+
require 'mime/types'
|
8
|
+
require 'thor'
|
9
|
+
|
10
|
+
module S3Website
|
11
|
+
DEFAULT_GZIP_EXTENSIONS = %w(.html .css .js .svg .txt)
|
12
|
+
end
|
13
|
+
|
14
|
+
%w{errors upload uploader tasks config_loader retry keyboard diff_helper endpoint parallelism}.each do |file|
|
15
|
+
require File.dirname(__FILE__) + "/s3_website/#{file}"
|
16
|
+
end
|
17
|
+
|
18
|
+
%w{invalidator}.each do |file|
|
19
|
+
require File.dirname(__FILE__) + "/cloudfront/#{file}"
|
20
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module S3Website
|
2
|
+
class ConfigLoader
|
3
|
+
CONFIGURATION_FILE = 's3_website.yml'
|
4
|
+
CONFIGURATION_FILE_TEMPLATE = <<-EOF
|
5
|
+
s3_id: YOUR_AWS_S3_ACCESS_KEY_ID
|
6
|
+
s3_secret: YOUR_AWS_S3_SECRET_ACCESS_KEY
|
7
|
+
s3_bucket: your.blog.bucket.com
|
8
|
+
EOF
|
9
|
+
|
10
|
+
def self.check_project(site_dir)
|
11
|
+
raise NotAJekyllProjectError unless File.directory?(site_dir)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Raise NoConfigurationFileError if the configuration file does not exists
|
15
|
+
def self.check_s3_configuration(site_dir)
|
16
|
+
unless File.exists?(get_configuration_file(site_dir))
|
17
|
+
create_template_configuration_file site_dir
|
18
|
+
raise NoConfigurationFileError
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Load configuration from s3_website.yml
|
25
|
+
# Raise MalformedConfigurationFileError if the configuration file does not contain the keys we expect
|
26
|
+
def self.load_configuration(site_dir)
|
27
|
+
config = load_yaml_file_and_validate site_dir
|
28
|
+
return config
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.create_template_configuration_file(site_dir)
|
32
|
+
File.open(get_configuration_file(site_dir), 'w') { |f|
|
33
|
+
f.write(CONFIGURATION_FILE_TEMPLATE)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.load_yaml_file_and_validate(site_dir)
|
38
|
+
begin
|
39
|
+
config = YAML.load(Erubis::Eruby.new(File.read(get_configuration_file(site_dir))).result)
|
40
|
+
rescue Exception
|
41
|
+
raise MalformedConfigurationFileError
|
42
|
+
end
|
43
|
+
raise MalformedConfigurationFileError unless config
|
44
|
+
raise MalformedConfigurationFileError if
|
45
|
+
['s3_bucket'].any? { |key|
|
46
|
+
mandatory_config_value = config[key]
|
47
|
+
mandatory_config_value.nil? || mandatory_config_value == ''
|
48
|
+
}
|
49
|
+
config
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.get_configuration_file(site_dir)
|
53
|
+
"#{site_dir}/../#{CONFIGURATION_FILE}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module S3Website
|
2
|
+
class DiffHelper
|
3
|
+
def self.resolve_files_to_upload(s3_bucket, site_dir)
|
4
|
+
s3_data_source = Filey::DataSources::AwsSdkS3.new(s3_bucket)
|
5
|
+
fs_data_source = Filey::DataSources::FileSystem.new(site_dir)
|
6
|
+
changed_local_files =
|
7
|
+
Filey::Comparison.list_changed(fs_data_source, s3_data_source)
|
8
|
+
new_local_files =
|
9
|
+
Filey::Comparison.list_missing(fs_data_source, s3_data_source)
|
10
|
+
[ normalise(changed_local_files), normalise(new_local_files) ]
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def self.normalise(fileys)
|
16
|
+
fileys.map { |filey|
|
17
|
+
filey.full_path.sub(/\.\//, '')
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module S3Website
|
2
|
+
class Endpoint
|
3
|
+
DEFAULT_LOCATION_CONSTRAINT = 'us-east-1'
|
4
|
+
attr_reader :region, :location_constraint, :hostname, :website_hostname
|
5
|
+
|
6
|
+
def initialize(location_constraint=nil)
|
7
|
+
location_constraint = DEFAULT_LOCATION_CONSTRAINT if location_constraint.nil?
|
8
|
+
raise "Invalid S3 location constraint #{location_constraint}" unless
|
9
|
+
location_constraints.has_key?location_constraint
|
10
|
+
@region = location_constraints.fetch(location_constraint)[:region]
|
11
|
+
@hostname = location_constraints.fetch(location_constraint)[:endpoint]
|
12
|
+
@website_hostname = location_constraints.fetch(location_constraint)[:website_hostname]
|
13
|
+
@location_constraint = location_constraint
|
14
|
+
end
|
15
|
+
|
16
|
+
# http://docs.amazonwebservices.com/general/latest/gr/rande.html#s3_region
|
17
|
+
def location_constraints
|
18
|
+
{
|
19
|
+
'us-east-1' => { :region => 'US Standard', :website_hostname => 's3-website-us-east-1.amazonaws.com', :endpoint => 's3.amazonaws.com' },
|
20
|
+
'us-west-2' => { :region => 'US West (Oregon)', :website_hostname => 's3-website-us-west-2.amazonaws.com', :endpoint => 's3-us-west-2.amazonaws.com' },
|
21
|
+
'us-west-1' => { :region => 'US West (Northern California)', :website_hostname => 's3-website-us-west-1.amazonaws.com', :endpoint => 's3-us-west-1.amazonaws.com' },
|
22
|
+
'EU' => { :region => 'EU (Ireland)', :website_hostname => 's3-website-eu-west-1.amazonaws.com', :endpoint => 's3-eu-west-1.amazonaws.com' },
|
23
|
+
'ap-southeast-1' => { :region => 'Asia Pacific (Singapore)', :website_hostname => 's3-website-ap-southeast-1.amazonaws.com', :endpoint => 's3-ap-southeast-1.amazonaws.com' },
|
24
|
+
'ap-southeast-2' => { :region => 'Asia Pacific (Sydney)', :website_hostname => 's3-website-ap-southeast-2.amazonaws.com', :endpoint => 's3-ap-southeast-2.amazonaws.com' },
|
25
|
+
'ap-northeast-1' => { :region => 'Asia Pacific (Tokyo)', :website_hostname => 's3-website-ap-northeast-1.amazonaws.com', :endpoint => 's3-ap-northeast-1.amazonaws.com' },
|
26
|
+
'sa-east-1' => { :region => 'South America (Sao Paulo)', :website_hostname => 's3-website-sa-east-1.amazonaws.com', :endpoint => 's3-sa-east-1.amazonaws.com' }
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module S3Website
|
2
|
+
class S3WebsiteError < StandardError
|
3
|
+
end
|
4
|
+
|
5
|
+
class NotAJekyllProjectError < S3WebsiteError
|
6
|
+
def initialize(message = "I can't find any directory called _site. Are you in the right directory?")
|
7
|
+
super(message)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class NoConfigurationFileError < S3WebsiteError
|
12
|
+
def initialize(message = "I've just generated a file called s3_website.yml. Go put your details in it!")
|
13
|
+
super(message)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class MalformedConfigurationFileError < S3WebsiteError
|
18
|
+
def initialize(message = "I can't parse the file s3_website.yml. It should look like this:\n#{ConfigLoader::CONFIGURATION_FILE_TEMPLATE}")
|
19
|
+
super(message)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class RetryAttemptsExhaustedError < S3WebsiteError
|
24
|
+
def initialize(message = "Operation failed even though we tried to recover from it")
|
25
|
+
super(message)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module S3Website
|
2
|
+
class Keyboard
|
3
|
+
def self.if_user_confirms_delete(to_delete, standard_input=STDIN)
|
4
|
+
delete_all = false
|
5
|
+
keep_all = false
|
6
|
+
confirmed_deletes = to_delete.map do |f|
|
7
|
+
delete = false
|
8
|
+
keep = false
|
9
|
+
until delete || delete_all || keep || keep_all
|
10
|
+
puts "#{f} is on S3 but not in your _site directory anymore. Do you want to [d]elete, [D]elete all, [k]eep, [K]eep all?"
|
11
|
+
case standard_input.gets.chomp
|
12
|
+
when 'd' then delete = true
|
13
|
+
when 'D' then delete_all = true
|
14
|
+
when 'k' then keep = true
|
15
|
+
when 'K' then keep_all = true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
if (delete_all || delete) && !(keep_all || keep)
|
19
|
+
f
|
20
|
+
end
|
21
|
+
end.select { |f| f }
|
22
|
+
Parallelism.each_in_parallel_or_sequentially(confirmed_deletes) { |f|
|
23
|
+
yield f
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|