middleman-s3_sync 4.0.3 → 4.6.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.
@@ -4,12 +4,19 @@ module Middleman
4
4
  attr_accessor :path, :resource, :partial_s3_resource, :full_s3_resource, :content_type, :gzipped, :options
5
5
 
6
6
  CONTENT_MD5_KEY = 'x-amz-meta-content-md5'
7
+ REDIRECT_KEY = 'x-amz-website-redirect-location'
7
8
 
8
9
  include Status
9
10
 
10
11
  def initialize(resource, partial_s3_resource)
11
12
  @resource = resource
12
- @path = resource ? resource.destination_path : partial_s3_resource.key
13
+ @path = if resource
14
+ resource.destination_path.sub(/^\//, '')
15
+ elsif partial_s3_resource&.key
16
+ partial_s3_resource.key.sub(/^\//, '')
17
+ else
18
+ ''
19
+ end
13
20
  @partial_s3_resource = partial_s3_resource
14
21
  end
15
22
 
@@ -19,13 +26,33 @@ module Middleman
19
26
 
20
27
  # S3 resource as returned by a HEAD request
21
28
  def full_s3_resource
22
- @full_s3_resource ||= bucket.files.head(remote_path)
29
+ @full_s3_resource ||= begin
30
+ bucket.object(remote_path.sub(/^\//, '')).head
31
+ rescue Aws::S3::Errors::NotFound
32
+ nil
33
+ end
23
34
  end
24
35
 
25
36
  def remote_path
26
- s3_resource ? s3_resource.key : "#{options.prefix}#{path}"
37
+ if s3_resource
38
+ if s3_resource.respond_to?(:key)
39
+ s3_resource.key.sub(/^\//, '')
40
+ else
41
+ # For HeadObjectOutput objects which don't have key method
42
+ options.prefix ? normalize_path(options.prefix, path) : path.sub(/^\//, '')
43
+ end
44
+ else
45
+ options.prefix ? normalize_path(options.prefix, path) : path.sub(/^\//, '')
46
+ end.sub(/^\//, '') # Ensure no leading slash
27
47
  end
28
48
  alias :key :remote_path
49
+
50
+ def normalize_path(prefix, path)
51
+ # Remove any trailing slash from prefix and leading slash from path
52
+ prefix = prefix.chomp('/')
53
+ path = path.sub(/^\//, '')
54
+ "#{prefix}/#{path}"
55
+ end
29
56
 
30
57
  def to_h
31
58
  attributes = {
@@ -52,18 +79,19 @@ module Middleman
52
79
  attributes[:encryption] = 'AES256'
53
80
  end
54
81
 
82
+ if redirect?
83
+ attributes[REDIRECT_KEY] = redirect_url
84
+ end
85
+
55
86
  attributes
56
87
  end
57
88
  alias :attributes :to_h
58
89
 
59
90
  def update!
60
- local_content { |body|
61
- say_status "#{ANSI.blue{"Updating"}} #{remote_path}#{ gzipped ? ANSI.white {' (gzipped)'} : ''}"
62
- s3_resource.merge_attributes(to_h)
63
- s3_resource.body = body
64
-
65
- s3_resource.save unless options.dry_run
66
- }
91
+ say_status "#{ANSI.blue{"Updating"}} #{remote_path}#{ gzipped ? ANSI.white {' (gzipped)'} : ''}"
92
+ unless options.dry_run
93
+ upload!
94
+ end
67
95
  end
68
96
 
69
97
  def local_path
@@ -77,14 +105,52 @@ module Middleman
77
105
 
78
106
  def destroy!
79
107
  say_status "#{ANSI.red{"Deleting"}} #{remote_path}"
80
- bucket.files.destroy remote_path unless options.dry_run
108
+ bucket.object(remote_path.sub(/^\//, '')).delete unless options.dry_run
81
109
  end
82
110
 
83
111
  def create!
84
112
  say_status "#{ANSI.green{"Creating"}} #{remote_path}#{ gzipped ? ANSI.white {' (gzipped)'} : ''}"
85
- local_content { |body|
86
- bucket.files.create(to_h.merge(body: body)) unless options.dry_run
113
+ unless options.dry_run
114
+ upload!
115
+ end
116
+ end
117
+
118
+ def upload!
119
+ object = bucket.object(remote_path.sub(/^\//, ''))
120
+ upload_options = {
121
+ body: local_content,
122
+ content_type: content_type,
123
+ acl: options.acl
87
124
  }
125
+
126
+ # Add metadata if present
127
+ if local_content_md5
128
+ upload_options[:metadata] = { CONTENT_MD5_KEY => local_content_md5 }
129
+ end
130
+
131
+ # Add redirect if present
132
+ upload_options[:website_redirect_location] = redirect_url if redirect?
133
+
134
+ # Add content encoding if present
135
+ upload_options[:content_encoding] = "gzip" if options.prefer_gzip && gzipped
136
+
137
+ # Add cache control and expires if present
138
+ if caching_policy
139
+ upload_options[:cache_control] = caching_policy.cache_control
140
+ upload_options[:expires] = caching_policy.expires
141
+ end
142
+
143
+ # Add storage class if needed
144
+ if options.reduced_redundancy_storage
145
+ upload_options[:storage_class] = 'REDUCED_REDUNDANCY'
146
+ end
147
+
148
+ # Add encryption if needed
149
+ if options.encryption
150
+ upload_options[:server_side_encryption] = 'AES256'
151
+ end
152
+
153
+ object.put(upload_options)
88
154
  end
89
155
 
90
156
  def ignore!
@@ -122,12 +188,18 @@ module Middleman
122
188
  status == :ignored || status == :alternate_encoding
123
189
  end
124
190
 
125
- def local_content(&block)
126
- File.open(local_path, &block)
191
+ def local_content
192
+ if block_given?
193
+ File.open(local_path) { |f| yield f.read }
194
+ else
195
+ File.read(local_path)
196
+ end
127
197
  end
128
198
 
129
199
  def status
130
- @status ||= if directory?
200
+ @status ||= if shunned?
201
+ :ignored
202
+ elsif directory?
131
203
  if remote?
132
204
  :deleted
133
205
  else
@@ -136,7 +208,7 @@ module Middleman
136
208
  elsif local? && remote?
137
209
  if options.force
138
210
  :updated
139
- elsif not caching_policy_match?
211
+ elsif not metadata_match?
140
212
  :updated
141
213
  elsif local_object_md5 == remote_object_md5
142
214
  :identical
@@ -170,11 +242,36 @@ module Middleman
170
242
  end
171
243
 
172
244
  def remote?
173
- !!s3_resource
245
+ !full_s3_resource.nil?
174
246
  end
175
247
 
176
248
  def redirect?
177
- full_s3_resource && full_s3_resource.metadata.has_key?('x-amz-website-redirect-location')
249
+ (resource && resource.respond_to?(:redirect?) && resource.redirect?) ||
250
+ (full_s3_resource && full_s3_resource.respond_to?(:website_redirect_location) && full_s3_resource.website_redirect_location)
251
+ end
252
+
253
+ def metadata_match?
254
+ redirect_match? && caching_policy_match?
255
+ end
256
+
257
+ def redirect_match?
258
+ if redirect?
259
+ redirect_url == remote_redirect_url
260
+ else
261
+ true
262
+ end
263
+ end
264
+
265
+ def shunned?
266
+ !!path[Regexp.union(options.ignore_paths)]
267
+ end
268
+
269
+ def remote_redirect_url
270
+ full_s3_resource&.website_redirect_location
271
+ end
272
+
273
+ def redirect_url
274
+ resource.respond_to?(:target_url) ? resource.target_url : nil
178
275
  end
179
276
 
180
277
  def directory?
@@ -186,7 +283,7 @@ module Middleman
186
283
  end
187
284
 
188
285
  def remote_object_md5
189
- s3_resource.etag
286
+ s3_resource.etag.gsub(/"/, '') if s3_resource.etag
190
287
  end
191
288
 
192
289
  def encoding_match?
@@ -194,7 +291,9 @@ module Middleman
194
291
  end
195
292
 
196
293
  def remote_content_md5
197
- full_s3_resource.metadata[CONTENT_MD5_KEY]
294
+ if full_s3_resource && full_s3_resource.metadata
295
+ full_s3_resource.metadata[CONTENT_MD5_KEY]
296
+ end
198
297
  end
199
298
 
200
299
  def local_object_md5
@@ -202,7 +301,13 @@ module Middleman
202
301
  end
203
302
 
204
303
  def local_content_md5
205
- @local_content_md5 ||= Digest::MD5.hexdigest(File.read(original_path))
304
+ @local_content_md5 ||= begin
305
+ if File.exist?(original_path)
306
+ Digest::MD5.hexdigest(File.read(original_path))
307
+ else
308
+ nil
309
+ end
310
+ end
206
311
  end
207
312
 
208
313
  def original_path
@@ -211,7 +316,7 @@ module Middleman
211
316
 
212
317
  def content_type
213
318
  @content_type ||= Middleman::S3Sync.content_types[local_path]
214
- @content_type ||= !resource.nil? ? resource.content_type : nil
319
+ @content_type ||= !resource.nil? && resource.respond_to?(:content_type) ? resource.content_type : nil
215
320
  end
216
321
 
217
322
  def caching_policy
@@ -219,7 +324,7 @@ module Middleman
219
324
  end
220
325
 
221
326
  def caching_policy_match?
222
- if (caching_policy)
327
+ if caching_policy && full_s3_resource && full_s3_resource.respond_to?(:cache_control)
223
328
  caching_policy.cache_control == full_s3_resource.cache_control
224
329
  else
225
330
  true
@@ -1,5 +1,5 @@
1
1
  module Middleman
2
2
  module S3Sync
3
- VERSION = "4.0.3"
3
+ VERSION = "4.6.0"
4
4
  end
5
5
  end
@@ -1,12 +1,13 @@
1
- require 'fog/aws'
2
- require 'fog/aws/storage'
1
+ require 'aws-sdk-s3'
3
2
  require 'digest/md5'
4
3
  require 'middleman/s3_sync/version'
5
4
  require 'middleman/s3_sync/options'
6
5
  require 'middleman/s3_sync/caching_policy'
7
6
  require 'middleman/s3_sync/status'
8
7
  require 'middleman/s3_sync/resource'
8
+ require 'middleman/s3_sync/cloudfront'
9
9
  require 'middleman-s3_sync/extension'
10
+ require 'middleman/redirect'
10
11
  require 'parallel'
11
12
  require 'ruby-progressbar'
12
13
  require 'thread'
@@ -24,12 +25,23 @@ module Middleman
24
25
  attr_accessor :mm_resources
25
26
  attr_reader :app
26
27
 
28
+ THREADS_COUNT = 8
29
+
30
+ # Track paths that were changed during sync for CloudFront invalidation
31
+ attr_accessor :invalidation_paths
32
+
27
33
  def sync()
28
34
  @app ||= ::Middleman::Application.new
35
+ @invalidation_paths = []
29
36
 
30
37
  say_status "Let's see if there's work to be done..."
31
38
  unless work_to_be_done?
32
39
  say_status "All S3 files are up to date."
40
+
41
+ # Still run CloudFront invalidation if requested for all paths
42
+ if s3_sync_options.cloudfront_invalidate && s3_sync_options.cloudfront_invalidate_all
43
+ CloudFront.invalidate([], s3_sync_options)
44
+ end
33
45
  return
34
46
  end
35
47
 
@@ -43,13 +55,18 @@ module Middleman
43
55
  create_resources
44
56
  update_resources
45
57
  delete_resources
58
+
59
+ # Invalidate CloudFront cache if requested
60
+ if s3_sync_options.cloudfront_invalidate
61
+ CloudFront.invalidate(@invalidation_paths, s3_sync_options)
62
+ end
46
63
  end
47
64
 
48
65
  def bucket
49
66
  @@bucket_lock.synchronize do
50
67
  @bucket ||= begin
51
- bucket = connection.directories.get(s3_sync_options.bucket, :prefix => s3_sync_options.prefix)
52
- raise "Bucket #{s3_sync_options.bucket} doesn't exist!" unless bucket
68
+ bucket = s3_resource.bucket(s3_sync_options.bucket)
69
+ raise "Bucket #{s3_sync_options.bucket} doesn't exist!" unless bucket.exists?
53
70
  bucket
54
71
  end
55
72
  end
@@ -58,6 +75,13 @@ module Middleman
58
75
  def add_local_resource(mm_resource)
59
76
  s3_sync_resources[mm_resource.destination_path] = S3Sync::Resource.new(mm_resource, remote_resource_for_path(mm_resource.destination_path)).tap(&:status)
60
77
  end
78
+
79
+ def add_invalidation_path(path)
80
+ @invalidation_paths ||= []
81
+ # Normalize path for CloudFront (ensure it starts with /)
82
+ normalized_path = path.start_with?('/') ? path : "/#{path}"
83
+ @invalidation_paths << normalized_path unless @invalidation_paths.include?(normalized_path)
84
+ end
61
85
 
62
86
  def remote_only_paths
63
87
  paths - s3_sync_resources.keys
@@ -73,7 +97,12 @@ module Middleman
73
97
 
74
98
  protected
75
99
  def update_bucket_versioning
76
- connection.put_bucket_versioning(s3_sync_options.bucket, "Enabled") if s3_sync_options.version_bucket
100
+ s3_client.put_bucket_versioning({
101
+ bucket: s3_sync_options.bucket,
102
+ versioning_configuration: {
103
+ status: "Enabled"
104
+ }
105
+ }) if s3_sync_options.version_bucket
77
106
  end
78
107
 
79
108
  def update_bucket_website
@@ -87,30 +116,47 @@ module Middleman
87
116
 
88
117
  unless opts.empty?
89
118
  say_status "Putting bucket website: #{opts.to_json}"
90
- connection.put_bucket_website(s3_sync_options.bucket, opts)
119
+ s3_client.put_bucket_website({
120
+ bucket: s3_sync_options.bucket,
121
+ website_configuration: opts
122
+ })
91
123
  end
92
124
  end
93
125
 
94
- def connection
95
- connection_options = {
96
- :region => s3_sync_options.region,
97
- :path_style => s3_sync_options.path_style
98
- }
126
+ def s3_client
127
+ @s3_client ||= Aws::S3::Client.new(connection_options)
128
+ end
99
129
 
100
- if s3_sync_options.aws_access_key_id && s3_sync_options.aws_secret_access_key
101
- connection_options.merge!({
102
- :aws_access_key_id => s3_sync_options.aws_access_key_id,
103
- :aws_secret_access_key => s3_sync_options.aws_secret_access_key
104
- })
105
- else
106
- connection_options.merge!({ :use_iam_profile => true })
107
- end
130
+ def s3_resource
131
+ @s3_resource ||= Aws::S3::Resource.new(client: s3_client)
132
+ end
133
+
134
+ def connection_options
135
+ @connection_options ||= begin
136
+ connection_options = {
137
+ endpoint: s3_sync_options.endpoint,
138
+ region: s3_sync_options.region,
139
+ force_path_style: s3_sync_options.path_style
140
+ }
141
+
142
+ if s3_sync_options.aws_access_key_id && s3_sync_options.aws_secret_access_key
143
+ connection_options.merge!({
144
+ access_key_id: s3_sync_options.aws_access_key_id,
145
+ secret_access_key: s3_sync_options.aws_secret_access_key
146
+ })
108
147
 
109
- @connection ||= Fog::Storage::AWS.new(connection_options)
148
+ # If using an assumed role
149
+ connection_options.merge!({
150
+ session_token: s3_sync_options.aws_session_token
151
+ }) if s3_sync_options.aws_session_token
152
+ end
153
+
154
+ connection_options
155
+ end
110
156
  end
111
157
 
112
158
  def remote_resource_for_path(path)
113
- bucket_files.find { |f| f.key == "#{s3_sync_options.prefix}#{path}" }
159
+ bucket_files[path]
114
160
  end
115
161
 
116
162
  def s3_sync_resources
@@ -125,7 +171,7 @@ module Middleman
125
171
 
126
172
  def remote_paths
127
173
  @remote_paths ||= if s3_sync_options.delete
128
- bucket_files.map(&:key)
174
+ bucket_files.keys
129
175
  else
130
176
  []
131
177
  end
@@ -133,42 +179,45 @@ module Middleman
133
179
 
134
180
  def bucket_files
135
181
  @@bucket_files_lock.synchronize do
136
- @bucket_files ||= [].tap { |files|
137
- bucket.files.each { |f|
138
- files << f
139
- }
140
- }
182
+ @bucket_files ||= begin
183
+ files = {}
184
+ bucket.objects.each do |object|
185
+ files[object.key] = object
186
+ end
187
+ files
188
+ end
141
189
  end
142
190
  end
143
191
 
144
192
  def create_resources
145
- files_to_create.each do |r|
146
- r.create!
193
+ Parallel.map(files_to_create, in_threads: THREADS_COUNT) do |resource|
194
+ resource.create!
195
+ add_invalidation_path(resource.path)
147
196
  end
148
197
  end
149
198
 
150
199
  def update_resources
151
- files_to_update.each do |r|
152
- r.update!
200
+ Parallel.map(files_to_update, in_threads: THREADS_COUNT) do |resource|
201
+ resource.update!
202
+ add_invalidation_path(resource.path)
153
203
  end
154
204
  end
155
205
 
156
206
  def delete_resources
157
- files_to_delete.each do |r|
158
- r.destroy!
207
+ Parallel.map(files_to_delete, in_threads: THREADS_COUNT) do |resource|
208
+ resource.destroy!
209
+ add_invalidation_path(resource.path)
159
210
  end
160
211
  end
161
212
 
162
213
  def ignore_resources
163
- files_to_ignore.each do |r|
164
- r.ignore!
165
- end
214
+ Parallel.map(files_to_ignore, in_threads: THREADS_COUNT, &:ignore!)
166
215
  end
167
216
 
168
217
  def work_to_be_done?
169
- Parallel.each(mm_resources, in_threads: 8, progress: "Processing sitemap") { |mm_resource| add_local_resource(mm_resource) }
218
+ Parallel.each(mm_resources, in_threads: THREADS_COUNT, progress: "Processing sitemap") { |mm_resource| add_local_resource(mm_resource) }
170
219
 
171
- Parallel.each(remote_only_paths, in_threads: 8, progress: "Processing remote files") do |remote_path|
220
+ Parallel.each(remote_only_paths, in_threads: THREADS_COUNT, progress: "Processing remote files") do |remote_path|
172
221
  s3_sync_resources[remote_path] ||= S3Sync::Resource.new(nil, remote_resource_for_path(remote_path)).tap(&:status)
173
222
  end
174
223
 
@@ -65,13 +65,39 @@ module Middleman
65
65
  type: :string,
66
66
  desc: 'Print instrument messages.'
67
67
 
68
+ class_option :cloudfront_distribution_id,
69
+ aliases: '-d',
70
+ type: :string,
71
+ desc: 'CloudFront distribution ID for invalidation.'
72
+
73
+ class_option :cloudfront_invalidate,
74
+ aliases: '-c',
75
+ type: :boolean,
76
+ desc: 'Invalidate CloudFront cache after sync.'
77
+
78
+ class_option :cloudfront_invalidate_all,
79
+ aliases: '-a',
80
+ type: :boolean,
81
+ desc: 'Invalidate all paths (/*) instead of only changed files.'
82
+
83
+ class_option :cloudfront_invalidation_batch_size,
84
+ type: :numeric,
85
+ desc: 'Maximum number of paths to invalidate in a single request (default: 1000).'
86
+
87
+ class_option :cloudfront_wait,
88
+ aliases: '-w',
89
+ type: :boolean,
90
+ desc: 'Wait for CloudFront invalidation to complete before exiting.'
91
+
68
92
  def s3_sync
69
93
  env = options[:environment].to_s.to_sym
70
94
  verbose = options[:verbose] ? 0 : 1
71
95
  instrument = options[:instrument]
72
96
 
97
+ mode = options[:build] ? :build : :config
98
+
73
99
  ::Middleman::S3Sync.app = ::Middleman::Application.new do
74
- config[:mode] = :build
100
+ config[:mode] = mode
75
101
  config[:environment] = env
76
102
  ::Middleman::Logger.singleton(verbose, instrument)
77
103
  end
@@ -97,6 +123,11 @@ module Middleman
97
123
  s3_sync_options.prefix = s3_sync_options.prefix.end_with?('/') ? s3_sync_options.prefix : s3_sync_options.prefix + '/'
98
124
  end
99
125
  s3_sync_options.dry_run = options[:dry_run] if options[:dry_run]
126
+ s3_sync_options.cloudfront_distribution_id = options[:cloudfront_distribution_id] if options[:cloudfront_distribution_id]
127
+ s3_sync_options.cloudfront_invalidate = options[:cloudfront_invalidate] if options[:cloudfront_invalidate]
128
+ s3_sync_options.cloudfront_invalidate_all = options[:cloudfront_invalidate_all] if options[:cloudfront_invalidate_all]
129
+ s3_sync_options.cloudfront_invalidation_batch_size = options[:cloudfront_invalidation_batch_size] if options[:cloudfront_invalidation_batch_size]
130
+ s3_sync_options.cloudfront_wait = options[:cloudfront_wait] if options[:cloudfront_wait]
100
131
 
101
132
  ::Middleman::S3Sync.sync()
102
133
  end
@@ -9,9 +9,11 @@ module Middleman
9
9
  option :http_prefix, nil, 'Path prefix of the resources'
10
10
  option :acl, 'public-read', 'ACL for the resources being pushed to S3'
11
11
  option :bucket, nil, 'The name of the bucket we are pushing to.'
12
+ option :endpoint, nil, 'The name of the endpoint to use - useful when using S3 compatible storage'
12
13
  option :region, 'us-east-1', 'The name of the AWS region hosting the S3 bucket'
13
14
  option :aws_access_key_id, ENV['AWS_ACCESS_KEY_ID'] , 'The AWS access key id'
14
15
  option :aws_secret_access_key, ENV['AWS_SECRET_ACCESS_KEY'], 'The AWS secret access key'
16
+ option :aws_session_token, ENV['AWS_SESSION_TOKEN'] || ENV['AWS_SECURITY_TOKEN'], 'The AWS session token (for assuming roles)'
15
17
  option :after_build, false, 'Whether to synchronize right after the build'
16
18
  option :build_dir, nil, 'Where the built site is stored'
17
19
  option :delete, true, 'Whether to delete resources that do not have a local equivalent'
@@ -26,6 +28,12 @@ module Middleman
26
28
  option :index_document, nil, 'S3 custom index document path'
27
29
  option :error_document, nil, 'S3 custom error document path'
28
30
  option :content_types, {}, 'Custom content types'
31
+ option :ignore_paths, [], 'Paths that should be ignored during sync, strings or regex are allowed'
32
+ option :cloudfront_distribution_id, nil, 'CloudFront distribution ID for invalidation'
33
+ option :cloudfront_invalidate, false, 'Whether to invalidate CloudFront cache after sync'
34
+ option :cloudfront_invalidate_all, false, 'Whether to invalidate all paths (/*) or only changed files'
35
+ option :cloudfront_invalidation_batch_size, 1000, 'Maximum number of paths to invalidate in a single request'
36
+ option :cloudfront_wait, false, 'Whether to wait for CloudFront invalidation to complete'
29
37
 
30
38
  expose_to_config :s3_sync_options, :default_caching_policy, :caching_policy
31
39
 
@@ -40,6 +48,7 @@ module Middleman
40
48
  read_config
41
49
  options.aws_access_key_id ||= ENV['AWS_ACCESS_KEY_ID']
42
50
  options.aws_secret_access_key ||= ENV['AWS_SECRET_ACCESS_KEY']
51
+ options.aws_session_token ||= ENV['AWS_SESSION_TOKEN'] || ENV['AWS_SECURITY_TOKEN']
43
52
  options.bucket ||= ENV['AWS_BUCKET']
44
53
  options.http_prefix = app.http_prefix if app.respond_to? :http_prefix
45
54
  options.build_dir ||= app.build_dir if app.respond_to? :build_dir
@@ -54,8 +63,15 @@ module Middleman
54
63
  ::Middleman::S3Sync.sync() if options.after_build
55
64
  end
56
65
 
57
- def manipulate_resource_list(mm_resources)
58
- ::Middleman::S3Sync.mm_resources = mm_resources
66
+ def manipulate_resource_list(resources)
67
+ ::Middleman::S3Sync.mm_resources = resources.each_with_object([]) do |resource, list|
68
+ next if resource.ignored?
69
+
70
+ list << resource
71
+ list << resource.target_resource if resource.respond_to?(:target_resource)
72
+ end
73
+
74
+ resources
59
75
  end
60
76
 
61
77
  def s3_sync_options
@@ -73,7 +89,7 @@ module Middleman
73
89
  config_file_path = File.join(root_path, ".s3_sync")
74
90
 
75
91
  # skip if config file does not exist
76
- return unless File.exists?(config_file_path)
92
+ return unless File.exist?(config_file_path)
77
93
 
78
94
  io = File.open(config_file_path, "r")
79
95
  end
@@ -1,3 +1,4 @@
1
+ require 'logger'
1
2
  require 'middleman-core'
2
3
  require 'middleman-s3_sync/commands'
3
4
  require 'middleman/s3_sync'
@@ -18,22 +18,26 @@ Gem::Specification.new do |gem|
18
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
19
  gem.require_paths = ["lib"]
20
20
 
21
- gem.add_runtime_dependency 'middleman-core', '>= 4.0.0'
21
+ gem.add_runtime_dependency 'middleman-core'
22
22
  gem.add_runtime_dependency 'middleman-cli'
23
23
  gem.add_runtime_dependency 'unf'
24
- gem.add_runtime_dependency 'fog-aws', '>= 0.1.1'
24
+ gem.add_runtime_dependency 'aws-sdk-s3'
25
+ gem.add_runtime_dependency 'aws-sdk-cloudfront'
25
26
  gem.add_runtime_dependency 'map'
26
27
  gem.add_runtime_dependency 'parallel'
27
28
  gem.add_runtime_dependency 'ruby-progressbar'
28
29
  gem.add_runtime_dependency 'ansi', '~> 1.5.0'
30
+ gem.add_runtime_dependency 'mime-types', '~> 3.1'
31
+ gem.add_runtime_dependency 'base64'
32
+ gem.add_runtime_dependency 'nokogiri', '>= 1.18.4'
29
33
 
30
34
  gem.add_development_dependency 'rake'
31
35
  gem.add_development_dependency 'pry'
32
36
  gem.add_development_dependency 'pry-byebug'
33
- gem.add_development_dependency 'rspec', '>= 3.0.0'
37
+ gem.add_development_dependency 'rspec'
38
+ gem.add_development_dependency 'rspec-support'
34
39
  gem.add_development_dependency 'rspec-its'
35
40
  gem.add_development_dependency 'rspec-mocks'
36
41
  gem.add_development_dependency 'timerizer'
37
- gem.add_development_dependency 'travis'
38
- gem.add_development_dependency 'travis-lint'
42
+ gem.add_development_dependency 'webrick'
39
43
  end