s3_website_monadic 0.0.15 → 0.0.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -0
- data/bin/s3_website_monadic +11 -5
- data/build.sbt +2 -0
- data/changelog.md +10 -0
- data/lib/s3_website.rb +3 -31
- data/resources/configuration_file_template.yml +0 -2
- data/s3_website.gemspec +1 -13
- data/src/main/scala/s3/website/S3.scala +4 -3
- metadata +2 -387
- data/features/as-library.feature +0 -29
- data/features/cassettes/cucumber_tags/create-redirect.yml +0 -384
- data/features/cassettes/cucumber_tags/empty-bucket.yml +0 -89
- data/features/cassettes/cucumber_tags/new-and-changed-files.yml +0 -303
- data/features/cassettes/cucumber_tags/new-files-for-sydney.yml +0 -211
- data/features/cassettes/cucumber_tags/new-files.yml +0 -355
- data/features/cassettes/cucumber_tags/no-new-or-changed-files.yml +0 -359
- data/features/cassettes/cucumber_tags/one-file-to-delete.yml +0 -390
- data/features/cassettes/cucumber_tags/only-changed-files.yml +0 -411
- data/features/cassettes/cucumber_tags/s3-and-cloudfront-after-deleting-a-file.yml +0 -434
- data/features/cassettes/cucumber_tags/s3-and-cloudfront-when-updating-a-file.yml +0 -435
- data/features/cassettes/cucumber_tags/s3-and-cloudfront.yml +0 -290
- data/features/cloudfront.feature +0 -54
- data/features/command-line-help.feature +0 -54
- data/features/delete.feature +0 -19
- data/features/error_reporting.feature +0 -24
- data/features/instructions-for-new-user.feature +0 -154
- data/features/jekyll-support.feature +0 -20
- data/features/nanoc-support.feature +0 -20
- data/features/push.feature +0 -115
- data/features/redirects.feature +0 -14
- data/features/security.feature +0 -15
- data/features/step_definitions/steps.rb +0 -86
- data/features/support/env.rb +0 -26
- data/features/support/test_site_dirs/cdn-powered.blog.fi/_site/css/styles.css +0 -3
- data/features/support/test_site_dirs/cdn-powered.blog.fi/_site/index.html +0 -5
- data/features/support/test_site_dirs/cdn-powered.blog.fi/s3_website.yml +0 -4
- data/features/support/test_site_dirs/cdn-powered.when-deleted-a-file.blog.fi/_site/index.html +0 -10
- data/features/support/test_site_dirs/cdn-powered.when-deleted-a-file.blog.fi/s3_website.yml +0 -5
- data/features/support/test_site_dirs/cdn-powered.with-one-change.blog.fi/_site/css/styles.css +0 -3
- data/features/support/test_site_dirs/cdn-powered.with-one-change.blog.fi/_site/index.html +0 -10
- data/features/support/test_site_dirs/cdn-powered.with-one-change.blog.fi/s3_website.yml +0 -4
- data/features/support/test_site_dirs/create-redirects/_site/.gitkeep +0 -0
- data/features/support/test_site_dirs/create-redirects/s3_website.yml +0 -6
- data/features/support/test_site_dirs/ignored-files.com/_site/css/styles.css +0 -4
- data/features/support/test_site_dirs/ignored-files.com/_site/index.html +0 -8
- data/features/support/test_site_dirs/ignored-files.com/s3_website.yml +0 -5
- 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 +0 -3
- data/features/support/test_site_dirs/index-and-assets.blog.fi/_site/index.html +0 -5
- data/features/support/test_site_dirs/index-and-assets.blog.fi/s3_website.yml +0 -3
- data/features/support/test_site_dirs/jekyllrb.com/_site/css/styles.css +0 -3
- data/features/support/test_site_dirs/jekyllrb.com/_site/index.html +0 -5
- data/features/support/test_site_dirs/jekyllrb.com/s3_website.yml +0 -3
- data/features/support/test_site_dirs/my.blog-with-clean-urls.com/_site/css/styles.css +0 -3
- data/features/support/test_site_dirs/my.blog-with-clean-urls.com/_site/index +0 -5
- data/features/support/test_site_dirs/my.blog-with-clean-urls.com/s3_website.yml +0 -3
- data/features/support/test_site_dirs/my.blog.com/_site/css/styles.css +0 -3
- data/features/support/test_site_dirs/my.blog.com/_site/index.html +0 -5
- data/features/support/test_site_dirs/my.blog.com/s3_website.yml +0 -3
- data/features/support/test_site_dirs/my.sydney.blog.au/_site/css/styles.css +0 -3
- data/features/support/test_site_dirs/my.sydney.blog.au/_site/index.html +0 -5
- data/features/support/test_site_dirs/my.sydney.blog.au/s3_website.yml +0 -4
- data/features/support/test_site_dirs/nanoc.ws/public/output/css/styles.css +0 -3
- data/features/support/test_site_dirs/nanoc.ws/public/output/index.html +0 -5
- data/features/support/test_site_dirs/nanoc.ws/s3_website.yml +0 -3
- data/features/support/test_site_dirs/new-and-changed-files.com/_site/css/styles.css +0 -4
- data/features/support/test_site_dirs/new-and-changed-files.com/_site/index.html +0 -8
- data/features/support/test_site_dirs/new-and-changed-files.com/s3_website.yml +0 -3
- data/features/support/test_site_dirs/no-new-or-changed-files.com/_site/css/styles.css +0 -3
- data/features/support/test_site_dirs/no-new-or-changed-files.com/_site/index.html +0 -5
- data/features/support/test_site_dirs/no-new-or-changed-files.com/s3_website.yml +0 -3
- data/features/support/test_site_dirs/only-changed-files.com/_site/css/styles.css +0 -3
- data/features/support/test_site_dirs/only-changed-files.com/_site/index.html +0 -9
- data/features/support/test_site_dirs/only-changed-files.com/s3_website.yml +0 -3
- data/features/support/test_site_dirs/site-that-contains-s3-website-file.com/_site/s3_website.yml +0 -3
- data/features/support/test_site_dirs/site-that-contains-s3-website-file.com/s3_website.yml +0 -3
- data/features/support/test_site_dirs/site-with-text-doc.com/_site/file.txt +0 -1
- data/features/support/test_site_dirs/site.with.css-maxage.com/_site/css/styles.css +0 -3
- data/features/support/test_site_dirs/site.with.css-maxage.com/_site/index.html +0 -5
- data/features/support/test_site_dirs/site.with.css-maxage.com/s3_website.yml +0 -5
- data/features/support/test_site_dirs/site.with.gzipped-and-max-aged-content.com/_site/css/styles.css +0 -3
- data/features/support/test_site_dirs/site.with.gzipped-and-max-aged-content.com/_site/index.html +0 -5
- data/features/support/test_site_dirs/site.with.gzipped-and-max-aged-content.com/s3_website.yml +0 -5
- data/features/support/test_site_dirs/site.with.gzipped-html.com/_site/css/styles.css +0 -3
- data/features/support/test_site_dirs/site.with.gzipped-html.com/_site/index.html +0 -5
- data/features/support/test_site_dirs/site.with.gzipped-html.com/s3_website.yml +0 -5
- data/features/support/test_site_dirs/site.with.maxage.com/_site/css/styles.css +0 -3
- data/features/support/test_site_dirs/site.with.maxage.com/_site/index.html +0 -5
- data/features/support/test_site_dirs/site.with.maxage.com/s3_website.yml +0 -4
- data/features/support/test_site_dirs/unpublish-a-post.com/_site/css/styles.css +0 -3
- data/features/support/test_site_dirs/unpublish-a-post.com/s3_website.yml +0 -3
- data/features/support/vcr.rb +0 -20
- data/features/website-performance.feature +0 -57
- data/lib/cloudfront/invalidator.rb +0 -37
- data/lib/s3_website/config_loader.rb +0 -55
- data/lib/s3_website/diff_helper.rb +0 -113
- data/lib/s3_website/endpoint.rb +0 -37
- data/lib/s3_website/errors.rb +0 -42
- data/lib/s3_website/keyboard.rb +0 -27
- data/lib/s3_website/parallelism.rb +0 -25
- data/lib/s3_website/retry.rb +0 -19
- data/lib/s3_website/tasks.rb +0 -36
- data/lib/s3_website/upload.rb +0 -137
- data/lib/s3_website/uploader.rb +0 -177
- data/spec/lib/cloudfront/invalidator_spec.rb +0 -60
- data/spec/lib/config_loader_spec.rb +0 -20
- data/spec/lib/endpoint_spec.rb +0 -31
- data/spec/lib/error_spec.rb +0 -21
- data/spec/lib/keyboard_spec.rb +0 -62
- data/spec/lib/parallelism_spec.rb +0 -81
- data/spec/lib/paths_spec.rb +0 -7
- data/spec/lib/retry_spec.rb +0 -34
- data/spec/lib/upload_spec.rb +0 -303
- data/spec/lib/uploader_spec.rb +0 -37
- data/spec/sample_files/hyde_site/_site/.vimrc +0 -5
- data/spec/sample_files/hyde_site/_site/css/styles.css +0 -3
- data/spec/sample_files/hyde_site/_site/index.html +0 -1
- data/spec/sample_files/hyde_site/s3_website.yml +0 -3
- data/spec/sample_files/tokyo_site/_site/.vimrc +0 -5
- data/spec/sample_files/tokyo_site/_site/css/styles.css +0 -3
- data/spec/sample_files/tokyo_site/_site/index.html +0 -1
- data/spec/sample_files/tokyo_site/s3_website.yml +0 -4
- data/spec/spec_helper.rb +0 -1
data/lib/s3_website/endpoint.rb
DELETED
@@ -1,37 +0,0 @@
|
|
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
|
-
eu_west_1_region = {
|
19
|
-
:region => 'EU (Ireland)',
|
20
|
-
:website_hostname => 's3-website-eu-west-1.amazonaws.com',
|
21
|
-
:endpoint => 's3-eu-west-1.amazonaws.com'
|
22
|
-
}
|
23
|
-
|
24
|
-
{
|
25
|
-
'us-east-1' => { :region => 'US Standard', :website_hostname => 's3-website-us-east-1.amazonaws.com', :endpoint => 's3.amazonaws.com' },
|
26
|
-
'us-west-2' => { :region => 'US West (Oregon)', :website_hostname => 's3-website-us-west-2.amazonaws.com', :endpoint => 's3-us-west-2.amazonaws.com' },
|
27
|
-
'us-west-1' => { :region => 'US West (Northern California)', :website_hostname => 's3-website-us-west-1.amazonaws.com', :endpoint => 's3-us-west-1.amazonaws.com' },
|
28
|
-
'EU' => eu_west_1_region,
|
29
|
-
'eu-west-1' => eu_west_1_region,
|
30
|
-
'ap-southeast-1' => { :region => 'Asia Pacific (Singapore)', :website_hostname => 's3-website-ap-southeast-1.amazonaws.com', :endpoint => 's3-ap-southeast-1.amazonaws.com' },
|
31
|
-
'ap-southeast-2' => { :region => 'Asia Pacific (Sydney)', :website_hostname => 's3-website-ap-southeast-2.amazonaws.com', :endpoint => 's3-ap-southeast-2.amazonaws.com' },
|
32
|
-
'ap-northeast-1' => { :region => 'Asia Pacific (Tokyo)', :website_hostname => 's3-website-ap-northeast-1.amazonaws.com', :endpoint => 's3-ap-northeast-1.amazonaws.com' },
|
33
|
-
'sa-east-1' => { :region => 'South America (Sao Paulo)', :website_hostname => 's3-website-sa-east-1.amazonaws.com', :endpoint => 's3-sa-east-1.amazonaws.com' }
|
34
|
-
}
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
data/lib/s3_website/errors.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
module S3Website
|
2
|
-
class S3WebsiteError < StandardError
|
3
|
-
end
|
4
|
-
|
5
|
-
class NoWebsiteDirectoryFound < S3WebsiteError
|
6
|
-
def initialize(message = "I can't find any website. Are you in the right directory?")
|
7
|
-
super(message)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
class NoPredefinedWebsiteDirectoryFound < NoWebsiteDirectoryFound
|
12
|
-
def initialize(message = "I can't find a website in any of the following directories: #{Paths.site_paths.join(', ')}. Please specify the location of the website with the --site option.")
|
13
|
-
super(message)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
class NoConfigurationFileError < S3WebsiteError
|
18
|
-
def initialize(message = "I've just generated a file called s3_website.yml. Go put your details in it!")
|
19
|
-
super(message)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
class MalformedConfigurationFileError < S3WebsiteError
|
24
|
-
def initialize(message = "I can't parse the file s3_website.yml. It should look like this:\n#{ConfigLoader.read_configuration_file_template}")
|
25
|
-
super(message)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class RetryAttemptsExhaustedError < S3WebsiteError
|
30
|
-
def initialize(message = "Operation failed even though we tried to recover from it")
|
31
|
-
super(message)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.error_report(error)
|
36
|
-
if error.is_a? S3WebsiteError
|
37
|
-
"#{error.message}"
|
38
|
-
else
|
39
|
-
"#{error.message} (#{error.class})"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
data/lib/s3_website/keyboard.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
module S3Website
|
2
|
-
class Keyboard
|
3
|
-
def self.if_user_confirms_delete(to_delete, config, 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 website 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, config) { |f|
|
23
|
-
yield f
|
24
|
-
}
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
module S3Website
|
2
|
-
class Parallelism
|
3
|
-
def self.each_in_parallel_or_sequentially(items, config, &operation)
|
4
|
-
if ENV['disable_parallel_processing']
|
5
|
-
items.each do |item|
|
6
|
-
operation.call item
|
7
|
-
end
|
8
|
-
else
|
9
|
-
slice_size = config['concurrency_level'] || DEFAULT_CONCURRENCY_LEVEL
|
10
|
-
items.each_slice(slice_size) { |items|
|
11
|
-
threads = items.map do |item|
|
12
|
-
Thread.new(item) { |item|
|
13
|
-
operation.call item
|
14
|
-
}
|
15
|
-
end
|
16
|
-
threads.each(&:join)
|
17
|
-
}
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
DEFAULT_CONCURRENCY_LEVEL = 3
|
24
|
-
end
|
25
|
-
end
|
data/lib/s3_website/retry.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
module S3Website
|
2
|
-
class Retry
|
3
|
-
def self.run_with_retry(sleep_milliseconds = 3.000)
|
4
|
-
attempt = 0
|
5
|
-
begin
|
6
|
-
yield
|
7
|
-
rescue Exception => e
|
8
|
-
$stderr.puts "Exception Occurred: #{e.message} (#{e.class}) Retrying in 3 seconds..."
|
9
|
-
sleep sleep_milliseconds
|
10
|
-
attempt += 1
|
11
|
-
if attempt <= 3
|
12
|
-
retry
|
13
|
-
else
|
14
|
-
raise RetryAttemptsExhaustedError
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
data/lib/s3_website/tasks.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
module S3Website
|
2
|
-
class Tasks
|
3
|
-
def self.push(config_file_dir, site_dir, in_headless_mode = false)
|
4
|
-
ConfigLoader.check_project site_dir
|
5
|
-
ConfigLoader.check_s3_configuration config_file_dir
|
6
|
-
config = S3Website::ConfigLoader.load_configuration config_file_dir
|
7
|
-
new_files_count, changed_files_count, deleted_files, changed_files, changed_redirects =
|
8
|
-
Uploader.run(site_dir, config, in_headless_mode)
|
9
|
-
invalidated_items_count =
|
10
|
-
invalidate_cf_dist_if_configured(config, changed_files + deleted_files + changed_redirects)
|
11
|
-
{
|
12
|
-
:new_files_count => new_files_count,
|
13
|
-
:changed_files_count => changed_files_count,
|
14
|
-
:deleted_files_count => deleted_files.size,
|
15
|
-
:invalidated_items_count => invalidated_items_count,
|
16
|
-
:changed_redirects_count => changed_redirects.size
|
17
|
-
}
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.config_create(dir)
|
21
|
-
ConfigLoader.check_s3_configuration dir
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def self.invalidate_cf_dist_if_configured(config, changed_files)
|
27
|
-
cloudfront_configured = config['cloudfront_distribution_id'] &&
|
28
|
-
(not config['cloudfront_distribution_id'].empty?)
|
29
|
-
invalidated_items_count = if cloudfront_configured
|
30
|
-
Cloudfront::Invalidator.invalidate(config, changed_files)
|
31
|
-
else
|
32
|
-
0
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
data/lib/s3_website/upload.rb
DELETED
@@ -1,137 +0,0 @@
|
|
1
|
-
require 'tempfile'
|
2
|
-
require 'zlib'
|
3
|
-
require 'zopfli'
|
4
|
-
|
5
|
-
module S3Website
|
6
|
-
class Upload
|
7
|
-
attr_reader :config, :file, :path, :full_path, :s3
|
8
|
-
BLACKLISTED_FILES = ['s3_website.yml']
|
9
|
-
|
10
|
-
def initialize(path, s3, config, site_dir)
|
11
|
-
raise "May not upload #{path}, because it's blacklisted" if Upload.is_blacklisted(path, config)
|
12
|
-
@path = path
|
13
|
-
@full_path = "#{site_dir}/#{path}"
|
14
|
-
@file = File.open("#{site_dir}/#{path}")
|
15
|
-
@s3 = s3
|
16
|
-
@config = config
|
17
|
-
end
|
18
|
-
|
19
|
-
def perform!
|
20
|
-
success = s3.buckets[config['s3_bucket']].objects[path].write(upload_file, upload_options)
|
21
|
-
upload_file.close
|
22
|
-
success
|
23
|
-
end
|
24
|
-
|
25
|
-
def details
|
26
|
-
"#{path}#{" [gzipped]" if gzip?}#{" [max-age=#{max_age}]" if cache_control?}"
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.is_blacklisted(path, config)
|
30
|
-
[
|
31
|
-
config['exclude_from_upload'],
|
32
|
-
BLACKLISTED_FILES
|
33
|
-
].flatten.compact.any? do |blacklisted_file|
|
34
|
-
Regexp.new(blacklisted_file).match path
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def upload_file
|
41
|
-
@upload_file ||= gzip? ? gzipped_file : file
|
42
|
-
end
|
43
|
-
|
44
|
-
def gzip?
|
45
|
-
return false unless !!config['gzip']
|
46
|
-
|
47
|
-
extensions = config['gzip'].is_a?(Array) ? config['gzip'] : S3Website::DEFAULT_GZIP_EXTENSIONS
|
48
|
-
extensions.include?(File.extname(path))
|
49
|
-
end
|
50
|
-
|
51
|
-
def gzipped_file
|
52
|
-
tempfile = Tempfile.new(File.basename(path))
|
53
|
-
tempfile.binmode
|
54
|
-
|
55
|
-
if config['gzip_zopfli']
|
56
|
-
gz_data = Zopfli.deflate file.read, format: :gzip
|
57
|
-
tempfile.write(gz_data)
|
58
|
-
tempfile.flush
|
59
|
-
else
|
60
|
-
gz = Zlib::GzipWriter.new(tempfile, Zlib::BEST_COMPRESSION, Zlib::DEFAULT_STRATEGY)
|
61
|
-
gz.mtime = File.mtime(full_path)
|
62
|
-
gz.orig_name = File.basename(path)
|
63
|
-
gz.write(file.read)
|
64
|
-
|
65
|
-
gz.flush
|
66
|
-
tempfile.flush
|
67
|
-
gz.close
|
68
|
-
end
|
69
|
-
tempfile.open
|
70
|
-
|
71
|
-
tempfile
|
72
|
-
end
|
73
|
-
|
74
|
-
def upload_options
|
75
|
-
opts = {}
|
76
|
-
opts[:reduced_redundancy] = config['s3_reduced_redundancy']
|
77
|
-
opts[:content_type] = resolve_content_type
|
78
|
-
opts[:content_encoding] = "gzip" if gzip?
|
79
|
-
opts[:cache_control] = cache_control_value if cache_control?
|
80
|
-
opts
|
81
|
-
end
|
82
|
-
|
83
|
-
def resolve_content_type
|
84
|
-
is_text = mime_type.start_with?('text/') || mime_type == 'application/json'
|
85
|
-
if is_text
|
86
|
-
"#{mime_type}; charset=utf-8" # Let's consider UTF-8 as the de-facto encoding standard for text
|
87
|
-
else
|
88
|
-
mime_type
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def cache_control_value
|
93
|
-
if max_age == 0
|
94
|
-
"no-cache, max-age=0"
|
95
|
-
else
|
96
|
-
"max-age=#{max_age}"
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def cache_control?
|
101
|
-
!!config['max_age']
|
102
|
-
end
|
103
|
-
|
104
|
-
def max_age
|
105
|
-
if config['max_age'].is_a?(Hash)
|
106
|
-
max_age_entries_most_specific_first.each do |glob_and_age|
|
107
|
-
(glob, age) = glob_and_age
|
108
|
-
return age if File.fnmatch(glob, path)
|
109
|
-
end
|
110
|
-
else
|
111
|
-
return config['max_age']
|
112
|
-
end
|
113
|
-
|
114
|
-
return 0
|
115
|
-
end
|
116
|
-
|
117
|
-
# The most specific max-age glob == the longest glob
|
118
|
-
def max_age_entries_most_specific_first
|
119
|
-
sorted_by_glob_length = config['max_age'].
|
120
|
-
each_pair.
|
121
|
-
to_a.
|
122
|
-
sort_by do |glob_and_age|
|
123
|
-
(glob, age) = glob_and_age
|
124
|
-
sort_key = glob.length
|
125
|
-
end.
|
126
|
-
reverse
|
127
|
-
end
|
128
|
-
|
129
|
-
def mime_type
|
130
|
-
if !!config['extensionless_mime_type'] && File.extname(path) == ""
|
131
|
-
config['extensionless_mime_type']
|
132
|
-
else
|
133
|
-
MIME::Types.type_for(path).first.to_s
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
data/lib/s3_website/uploader.rb
DELETED
@@ -1,177 +0,0 @@
|
|
1
|
-
module S3Website
|
2
|
-
class Uploader
|
3
|
-
def self.run(site_dir, config, in_headless_mode = false)
|
4
|
-
puts "Deploying #{site_dir.sub(Dir.pwd + '/', '')}/* to #{config['s3_bucket']}"
|
5
|
-
|
6
|
-
s3_config = { :s3_endpoint => Endpoint.new(config['s3_endpoint']).hostname }
|
7
|
-
s3_id, s3_secret = config['s3_id'], config['s3_secret']
|
8
|
-
unless s3_id.nil? || s3_id == '' || s3_secret.nil? || s3_secret == ''
|
9
|
-
s3_config.merge! :access_key_id => s3_id, :secret_access_key => s3_secret
|
10
|
-
end
|
11
|
-
|
12
|
-
s3 = AWS::S3.new(s3_config)
|
13
|
-
|
14
|
-
new_files_count, changed_files_count, changed_files = upload_files(
|
15
|
-
s3, config, site_dir
|
16
|
-
)
|
17
|
-
|
18
|
-
redirects = config['redirects'] || {}
|
19
|
-
changed_redirects = setup_redirects redirects, config, s3
|
20
|
-
|
21
|
-
deleted_files = remove_superfluous_files(
|
22
|
-
s3,
|
23
|
-
config,
|
24
|
-
{
|
25
|
-
:s3_bucket => config['s3_bucket'],
|
26
|
-
:site_dir => site_dir,
|
27
|
-
:redirects => redirects,
|
28
|
-
:in_headless_mode => in_headless_mode,
|
29
|
-
:ignore_on_server => config["ignore_on_server"]
|
30
|
-
}
|
31
|
-
)
|
32
|
-
|
33
|
-
print_done_report config
|
34
|
-
|
35
|
-
[new_files_count, changed_files_count, deleted_files, changed_files, changed_redirects, deleted_files]
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def self.print_done_report(config)
|
41
|
-
bucket_name = config['s3_bucket']
|
42
|
-
website_hostname_suffix = Endpoint.new(config['s3_endpoint']).website_hostname
|
43
|
-
website_hostname_with_bucket =
|
44
|
-
"%s.%s" % [bucket_name, website_hostname_suffix]
|
45
|
-
puts "Done! Go visit: http://#{website_hostname_with_bucket}/index.html"
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.upload_files(s3, config, site_dir)
|
49
|
-
changed_files, new_files = DiffHelper.resolve_files_to_upload(
|
50
|
-
s3.buckets[config['s3_bucket']],
|
51
|
-
site_dir,
|
52
|
-
config
|
53
|
-
)
|
54
|
-
to_upload = changed_files + new_files
|
55
|
-
if to_upload.empty?
|
56
|
-
puts "No new or changed files to upload"
|
57
|
-
else
|
58
|
-
pre_upload_report = []
|
59
|
-
pre_upload_report << "Uploading"
|
60
|
-
pre_upload_report << "#{new_files.length} new" if new_files.length > 0
|
61
|
-
pre_upload_report << "and" if changed_files.length > 0 and new_files.length > 0
|
62
|
-
pre_upload_report << "#{changed_files.length} changed" if changed_files.length > 0
|
63
|
-
pre_upload_report << "file(s)"
|
64
|
-
puts pre_upload_report.join(' ')
|
65
|
-
upload_in_parallel_or_sequentially to_upload, s3, config, site_dir
|
66
|
-
end
|
67
|
-
[new_files.length, changed_files.length, changed_files]
|
68
|
-
end
|
69
|
-
|
70
|
-
def self.upload_in_parallel_or_sequentially(files_to_upload, s3, config, site_dir)
|
71
|
-
Parallelism.each_in_parallel_or_sequentially(files_to_upload, config) { |f|
|
72
|
-
upload_file(f, s3, config, site_dir)
|
73
|
-
}
|
74
|
-
end
|
75
|
-
|
76
|
-
def self.upload_file(file, s3, config, site_dir)
|
77
|
-
Retry.run_with_retry do
|
78
|
-
upload = Upload.new(file, s3, config, site_dir)
|
79
|
-
|
80
|
-
if upload.perform!
|
81
|
-
print "Upload #{upload.details}: Success!\n"
|
82
|
-
else
|
83
|
-
print "Upload #{upload.details}: FAILURE!\n"
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def self.setup_redirects(redirects, config, s3)
|
89
|
-
operations = redirects.map do |path, target|
|
90
|
-
setup_redirect(path, target, s3, config)
|
91
|
-
end
|
92
|
-
performed_operations = operations.reject do |op|
|
93
|
-
op == :no_redirect_operation_performed
|
94
|
-
end
|
95
|
-
unless performed_operations.empty?
|
96
|
-
puts 'Creating new redirects ...'
|
97
|
-
end
|
98
|
-
performed_operations.each do |redirect_operation|
|
99
|
-
puts ' ' + redirect_operation[:report]
|
100
|
-
end
|
101
|
-
performed_operations.map do |redirect_operation|
|
102
|
-
redirect_operation[:path]
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def self.setup_redirect(path, target, s3, config)
|
107
|
-
target = '/' + target unless target =~ %r{^(/|https?://)}
|
108
|
-
s3_object = s3.buckets[config['s3_bucket']].objects[path]
|
109
|
-
|
110
|
-
begin
|
111
|
-
current_head = s3_object.head
|
112
|
-
rescue AWS::S3::Errors::NoSuchKey
|
113
|
-
end
|
114
|
-
|
115
|
-
if current_head.nil? or current_head[:website_redirect_location] != target
|
116
|
-
s3_object.write('', :website_redirect_location => target)
|
117
|
-
{
|
118
|
-
:report => "Redirect #{path} to #{target}: Success!",
|
119
|
-
:path => path
|
120
|
-
}
|
121
|
-
else
|
122
|
-
:no_redirect_operation_performed
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
def self.remove_superfluous_files(s3, config, options)
|
127
|
-
s3_bucket_name = options.fetch(:s3_bucket)
|
128
|
-
site_dir = options.fetch(:site_dir)
|
129
|
-
in_headless_mode = options.fetch(:in_headless_mode)
|
130
|
-
|
131
|
-
remote_files = s3.buckets[s3_bucket_name].objects.map { |f| f.key }
|
132
|
-
local_files = load_all_local_files(site_dir) + options.fetch(:redirects).keys
|
133
|
-
files_to_delete = build_list_of_files_to_delete(remote_files, local_files, options[:ignore_on_server])
|
134
|
-
|
135
|
-
deleted_files = []
|
136
|
-
if in_headless_mode
|
137
|
-
files_to_delete.each { |s3_object_key|
|
138
|
-
delete_s3_object s3, s3_bucket_name, s3_object_key
|
139
|
-
deleted_files << s3_object_key
|
140
|
-
}
|
141
|
-
else
|
142
|
-
Keyboard.if_user_confirms_delete(files_to_delete, config) { |s3_object_key|
|
143
|
-
delete_s3_object s3, s3_bucket_name, s3_object_key
|
144
|
-
deleted_files << s3_object_key
|
145
|
-
}
|
146
|
-
end
|
147
|
-
deleted_files
|
148
|
-
end
|
149
|
-
|
150
|
-
def self.build_list_of_files_to_delete(remote_files, local_files, ignore_on_server = nil)
|
151
|
-
files_to_delete = remote_files - local_files
|
152
|
-
files_to_delete.reject { |file|
|
153
|
-
ignore_regexps(ignore_on_server).any? do |ignore_regexp|
|
154
|
-
Regexp.new(ignore_regexp).match file
|
155
|
-
end
|
156
|
-
}
|
157
|
-
end
|
158
|
-
|
159
|
-
def self.ignore_regexps(ignore_on_server)
|
160
|
-
ignore_regexps = ignore_on_server || "a_string_that_should_never_match_ever"
|
161
|
-
ignore_regexps.class == Array ? ignore_regexps : [ignore_regexps]
|
162
|
-
end
|
163
|
-
|
164
|
-
def self.delete_s3_object(s3, s3_bucket_name, s3_object_key)
|
165
|
-
Retry.run_with_retry do
|
166
|
-
s3.buckets[s3_bucket_name].objects[s3_object_key].delete
|
167
|
-
print "Delete #{s3_object_key}: Success!\n"
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
def self.load_all_local_files(site_dir)
|
172
|
-
Dir.glob(site_dir + '/**/*', File::FNM_DOTMATCH).
|
173
|
-
delete_if { |f| File.directory?(f) }.
|
174
|
-
map { |f| f.sub(site_dir + '/', '') }
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|