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.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -0
  3. data/bin/s3_website_monadic +11 -5
  4. data/build.sbt +2 -0
  5. data/changelog.md +10 -0
  6. data/lib/s3_website.rb +3 -31
  7. data/resources/configuration_file_template.yml +0 -2
  8. data/s3_website.gemspec +1 -13
  9. data/src/main/scala/s3/website/S3.scala +4 -3
  10. metadata +2 -387
  11. data/features/as-library.feature +0 -29
  12. data/features/cassettes/cucumber_tags/create-redirect.yml +0 -384
  13. data/features/cassettes/cucumber_tags/empty-bucket.yml +0 -89
  14. data/features/cassettes/cucumber_tags/new-and-changed-files.yml +0 -303
  15. data/features/cassettes/cucumber_tags/new-files-for-sydney.yml +0 -211
  16. data/features/cassettes/cucumber_tags/new-files.yml +0 -355
  17. data/features/cassettes/cucumber_tags/no-new-or-changed-files.yml +0 -359
  18. data/features/cassettes/cucumber_tags/one-file-to-delete.yml +0 -390
  19. data/features/cassettes/cucumber_tags/only-changed-files.yml +0 -411
  20. data/features/cassettes/cucumber_tags/s3-and-cloudfront-after-deleting-a-file.yml +0 -434
  21. data/features/cassettes/cucumber_tags/s3-and-cloudfront-when-updating-a-file.yml +0 -435
  22. data/features/cassettes/cucumber_tags/s3-and-cloudfront.yml +0 -290
  23. data/features/cloudfront.feature +0 -54
  24. data/features/command-line-help.feature +0 -54
  25. data/features/delete.feature +0 -19
  26. data/features/error_reporting.feature +0 -24
  27. data/features/instructions-for-new-user.feature +0 -154
  28. data/features/jekyll-support.feature +0 -20
  29. data/features/nanoc-support.feature +0 -20
  30. data/features/push.feature +0 -115
  31. data/features/redirects.feature +0 -14
  32. data/features/security.feature +0 -15
  33. data/features/step_definitions/steps.rb +0 -86
  34. data/features/support/env.rb +0 -26
  35. data/features/support/test_site_dirs/cdn-powered.blog.fi/_site/css/styles.css +0 -3
  36. data/features/support/test_site_dirs/cdn-powered.blog.fi/_site/index.html +0 -5
  37. data/features/support/test_site_dirs/cdn-powered.blog.fi/s3_website.yml +0 -4
  38. data/features/support/test_site_dirs/cdn-powered.when-deleted-a-file.blog.fi/_site/index.html +0 -10
  39. data/features/support/test_site_dirs/cdn-powered.when-deleted-a-file.blog.fi/s3_website.yml +0 -5
  40. data/features/support/test_site_dirs/cdn-powered.with-one-change.blog.fi/_site/css/styles.css +0 -3
  41. data/features/support/test_site_dirs/cdn-powered.with-one-change.blog.fi/_site/index.html +0 -10
  42. data/features/support/test_site_dirs/cdn-powered.with-one-change.blog.fi/s3_website.yml +0 -4
  43. data/features/support/test_site_dirs/create-redirects/_site/.gitkeep +0 -0
  44. data/features/support/test_site_dirs/create-redirects/s3_website.yml +0 -6
  45. data/features/support/test_site_dirs/ignored-files.com/_site/css/styles.css +0 -4
  46. data/features/support/test_site_dirs/ignored-files.com/_site/index.html +0 -8
  47. data/features/support/test_site_dirs/ignored-files.com/s3_website.yml +0 -5
  48. data/features/support/test_site_dirs/index-and-assets.blog.fi/_site/assets/picture.gif +0 -0
  49. data/features/support/test_site_dirs/index-and-assets.blog.fi/_site/css/styles.css +0 -3
  50. data/features/support/test_site_dirs/index-and-assets.blog.fi/_site/index.html +0 -5
  51. data/features/support/test_site_dirs/index-and-assets.blog.fi/s3_website.yml +0 -3
  52. data/features/support/test_site_dirs/jekyllrb.com/_site/css/styles.css +0 -3
  53. data/features/support/test_site_dirs/jekyllrb.com/_site/index.html +0 -5
  54. data/features/support/test_site_dirs/jekyllrb.com/s3_website.yml +0 -3
  55. data/features/support/test_site_dirs/my.blog-with-clean-urls.com/_site/css/styles.css +0 -3
  56. data/features/support/test_site_dirs/my.blog-with-clean-urls.com/_site/index +0 -5
  57. data/features/support/test_site_dirs/my.blog-with-clean-urls.com/s3_website.yml +0 -3
  58. data/features/support/test_site_dirs/my.blog.com/_site/css/styles.css +0 -3
  59. data/features/support/test_site_dirs/my.blog.com/_site/index.html +0 -5
  60. data/features/support/test_site_dirs/my.blog.com/s3_website.yml +0 -3
  61. data/features/support/test_site_dirs/my.sydney.blog.au/_site/css/styles.css +0 -3
  62. data/features/support/test_site_dirs/my.sydney.blog.au/_site/index.html +0 -5
  63. data/features/support/test_site_dirs/my.sydney.blog.au/s3_website.yml +0 -4
  64. data/features/support/test_site_dirs/nanoc.ws/public/output/css/styles.css +0 -3
  65. data/features/support/test_site_dirs/nanoc.ws/public/output/index.html +0 -5
  66. data/features/support/test_site_dirs/nanoc.ws/s3_website.yml +0 -3
  67. data/features/support/test_site_dirs/new-and-changed-files.com/_site/css/styles.css +0 -4
  68. data/features/support/test_site_dirs/new-and-changed-files.com/_site/index.html +0 -8
  69. data/features/support/test_site_dirs/new-and-changed-files.com/s3_website.yml +0 -3
  70. data/features/support/test_site_dirs/no-new-or-changed-files.com/_site/css/styles.css +0 -3
  71. data/features/support/test_site_dirs/no-new-or-changed-files.com/_site/index.html +0 -5
  72. data/features/support/test_site_dirs/no-new-or-changed-files.com/s3_website.yml +0 -3
  73. data/features/support/test_site_dirs/only-changed-files.com/_site/css/styles.css +0 -3
  74. data/features/support/test_site_dirs/only-changed-files.com/_site/index.html +0 -9
  75. data/features/support/test_site_dirs/only-changed-files.com/s3_website.yml +0 -3
  76. data/features/support/test_site_dirs/site-that-contains-s3-website-file.com/_site/s3_website.yml +0 -3
  77. data/features/support/test_site_dirs/site-that-contains-s3-website-file.com/s3_website.yml +0 -3
  78. data/features/support/test_site_dirs/site-with-text-doc.com/_site/file.txt +0 -1
  79. data/features/support/test_site_dirs/site.with.css-maxage.com/_site/css/styles.css +0 -3
  80. data/features/support/test_site_dirs/site.with.css-maxage.com/_site/index.html +0 -5
  81. data/features/support/test_site_dirs/site.with.css-maxage.com/s3_website.yml +0 -5
  82. data/features/support/test_site_dirs/site.with.gzipped-and-max-aged-content.com/_site/css/styles.css +0 -3
  83. data/features/support/test_site_dirs/site.with.gzipped-and-max-aged-content.com/_site/index.html +0 -5
  84. data/features/support/test_site_dirs/site.with.gzipped-and-max-aged-content.com/s3_website.yml +0 -5
  85. data/features/support/test_site_dirs/site.with.gzipped-html.com/_site/css/styles.css +0 -3
  86. data/features/support/test_site_dirs/site.with.gzipped-html.com/_site/index.html +0 -5
  87. data/features/support/test_site_dirs/site.with.gzipped-html.com/s3_website.yml +0 -5
  88. data/features/support/test_site_dirs/site.with.maxage.com/_site/css/styles.css +0 -3
  89. data/features/support/test_site_dirs/site.with.maxage.com/_site/index.html +0 -5
  90. data/features/support/test_site_dirs/site.with.maxage.com/s3_website.yml +0 -4
  91. data/features/support/test_site_dirs/unpublish-a-post.com/_site/css/styles.css +0 -3
  92. data/features/support/test_site_dirs/unpublish-a-post.com/s3_website.yml +0 -3
  93. data/features/support/vcr.rb +0 -20
  94. data/features/website-performance.feature +0 -57
  95. data/lib/cloudfront/invalidator.rb +0 -37
  96. data/lib/s3_website/config_loader.rb +0 -55
  97. data/lib/s3_website/diff_helper.rb +0 -113
  98. data/lib/s3_website/endpoint.rb +0 -37
  99. data/lib/s3_website/errors.rb +0 -42
  100. data/lib/s3_website/keyboard.rb +0 -27
  101. data/lib/s3_website/parallelism.rb +0 -25
  102. data/lib/s3_website/retry.rb +0 -19
  103. data/lib/s3_website/tasks.rb +0 -36
  104. data/lib/s3_website/upload.rb +0 -137
  105. data/lib/s3_website/uploader.rb +0 -177
  106. data/spec/lib/cloudfront/invalidator_spec.rb +0 -60
  107. data/spec/lib/config_loader_spec.rb +0 -20
  108. data/spec/lib/endpoint_spec.rb +0 -31
  109. data/spec/lib/error_spec.rb +0 -21
  110. data/spec/lib/keyboard_spec.rb +0 -62
  111. data/spec/lib/parallelism_spec.rb +0 -81
  112. data/spec/lib/paths_spec.rb +0 -7
  113. data/spec/lib/retry_spec.rb +0 -34
  114. data/spec/lib/upload_spec.rb +0 -303
  115. data/spec/lib/uploader_spec.rb +0 -37
  116. data/spec/sample_files/hyde_site/_site/.vimrc +0 -5
  117. data/spec/sample_files/hyde_site/_site/css/styles.css +0 -3
  118. data/spec/sample_files/hyde_site/_site/index.html +0 -1
  119. data/spec/sample_files/hyde_site/s3_website.yml +0 -3
  120. data/spec/sample_files/tokyo_site/_site/.vimrc +0 -5
  121. data/spec/sample_files/tokyo_site/_site/css/styles.css +0 -3
  122. data/spec/sample_files/tokyo_site/_site/index.html +0 -1
  123. data/spec/sample_files/tokyo_site/s3_website.yml +0 -4
  124. data/spec/spec_helper.rb +0 -1
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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