middleman-s3_sync 4.6.2 → 4.6.3
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.
- checksums.yaml +4 -4
- data/.mise.toml +17 -0
- data/Changelog.md +3 -0
- data/lib/middleman/s3_sync/options.rb +26 -3
- data/lib/middleman/s3_sync/resource.rb +56 -34
- data/lib/middleman/s3_sync/version.rb +1 -1
- data/lib/middleman-s3_sync/extension.rb +22 -1
- data/middleman-s3_sync.gemspec +2 -4
- data/spec/aws_sdk_parameters_spec.rb +110 -0
- data/spec/extension_spec.rb +114 -0
- data/spec/options_spec.rb +272 -0
- metadata +14 -37
- /data/{.envrc → .envrc.backup} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 575e3838865eccc6e585b35829b3891f2a86a77502df0da9e411634ad40312a1
|
|
4
|
+
data.tar.gz: bf897e1d7725bf536759469ef486dc81e51410c67f20b801322a1ad2403a8445
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1ef461a263b9ec005dcca856b34d3b02509550d2ba6d94904920bdf448268aacc547cbdf412406616f4ff409ce70e425ad013cd3e5e0de8a75315c1134d518e9
|
|
7
|
+
data.tar.gz: a00ae49ff314f27e8a3d6878ae00709a67c5759563675b64d72fdc5a61bdc057a19191ab04dea2795f80cfd75c86c00d0e99d707be382227c6c6eeabefeff2c6
|
data/.mise.toml
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[tools]
|
|
2
|
+
ruby = "3.4"
|
|
3
|
+
|
|
4
|
+
[env]
|
|
5
|
+
_.file = ".env"
|
|
6
|
+
_.path = ["./bin"]
|
|
7
|
+
|
|
8
|
+
[tasks.binstubs]
|
|
9
|
+
description = "Generate binstubs for all gems"
|
|
10
|
+
run = "bundle binstubs --all"
|
|
11
|
+
|
|
12
|
+
[tasks.setup]
|
|
13
|
+
description = "Setup project dependencies and binstubs"
|
|
14
|
+
run = [
|
|
15
|
+
"bundle install",
|
|
16
|
+
"mise run binstubs"
|
|
17
|
+
]
|
data/Changelog.md
CHANGED
|
@@ -10,10 +10,10 @@ module Middleman
|
|
|
10
10
|
:region,
|
|
11
11
|
:aws_access_key_id,
|
|
12
12
|
:aws_secret_access_key,
|
|
13
|
+
:aws_session_token,
|
|
13
14
|
:after_build,
|
|
14
15
|
:delete,
|
|
15
16
|
:encryption,
|
|
16
|
-
:existing_remote_file,
|
|
17
17
|
:build_dir,
|
|
18
18
|
:force,
|
|
19
19
|
:prefer_gzip,
|
|
@@ -25,12 +25,35 @@ module Middleman
|
|
|
25
25
|
:content_types,
|
|
26
26
|
:ignore_paths,
|
|
27
27
|
:index_document,
|
|
28
|
-
:error_document
|
|
28
|
+
:error_document,
|
|
29
|
+
:cloudfront_distribution_id,
|
|
30
|
+
:cloudfront_invalidate,
|
|
31
|
+
:cloudfront_invalidate_all,
|
|
32
|
+
:cloudfront_invalidation_batch_size,
|
|
33
|
+
:cloudfront_invalidation_max_retries,
|
|
34
|
+
:cloudfront_invalidation_batch_delay,
|
|
35
|
+
:cloudfront_wait
|
|
29
36
|
]
|
|
30
37
|
attr_accessor *OPTIONS
|
|
31
38
|
|
|
32
39
|
def acl
|
|
33
|
-
@acl
|
|
40
|
+
# If @acl is explicitly set to empty string or false, return nil (for buckets with ACLs disabled)
|
|
41
|
+
# If @acl is nil and was never set, return default 'public-read'
|
|
42
|
+
# Otherwise return the set value
|
|
43
|
+
return nil if @acl == '' || @acl == false
|
|
44
|
+
@acl_explicitly_set ? @acl : (@acl || 'public-read')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def acl=(value)
|
|
48
|
+
@acl_explicitly_set = true
|
|
49
|
+
@acl = value
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def acl_enabled?
|
|
53
|
+
# ACLs are disabled if explicitly set to nil, empty string, or false
|
|
54
|
+
return false if @acl_explicitly_set && (@acl.nil? || @acl == '' || @acl == false)
|
|
55
|
+
# Otherwise ACLs are enabled (using default or explicit value)
|
|
56
|
+
true
|
|
34
57
|
end
|
|
35
58
|
|
|
36
59
|
def aws_access_key_id=(aws_access_key_id)
|
|
@@ -55,10 +55,11 @@ module Middleman
|
|
|
55
55
|
def to_h
|
|
56
56
|
attributes = {
|
|
57
57
|
:key => key,
|
|
58
|
-
:acl => options.acl,
|
|
59
58
|
:content_type => content_type,
|
|
60
59
|
'content-md5' => local_content_md5
|
|
61
60
|
}
|
|
61
|
+
# Only add ACL if enabled (not for buckets with ACLs disabled)
|
|
62
|
+
attributes[:acl] = options.acl if options.acl_enabled?
|
|
62
63
|
|
|
63
64
|
if caching_policy
|
|
64
65
|
attributes[:cache_control] = caching_policy.cache_control
|
|
@@ -115,40 +116,22 @@ module Middleman
|
|
|
115
116
|
|
|
116
117
|
def upload!
|
|
117
118
|
object = bucket.object(remote_path.sub(/^\//, ''))
|
|
118
|
-
upload_options =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
upload_options[:content_encoding] = "gzip" if options.prefer_gzip && gzipped
|
|
134
|
-
|
|
135
|
-
# Add cache control and expires if present
|
|
136
|
-
if caching_policy
|
|
137
|
-
upload_options[:cache_control] = caching_policy.cache_control
|
|
138
|
-
upload_options[:expires] = caching_policy.expires
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
# Add storage class if needed
|
|
142
|
-
if options.reduced_redundancy_storage
|
|
143
|
-
upload_options[:storage_class] = 'REDUCED_REDUNDANCY'
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
# Add encryption if needed
|
|
147
|
-
if options.encryption
|
|
148
|
-
upload_options[:server_side_encryption] = 'AES256'
|
|
119
|
+
upload_options = build_upload_options
|
|
120
|
+
|
|
121
|
+
begin
|
|
122
|
+
object.put(upload_options)
|
|
123
|
+
rescue Aws::S3::Errors::AccessControlListNotSupported => e
|
|
124
|
+
# Bucket has ACLs disabled - retry without ACL
|
|
125
|
+
if upload_options.key?(:acl)
|
|
126
|
+
say_status "#{ANSI.yellow{"Note"}} Bucket does not support ACLs, retrying without ACL parameter"
|
|
127
|
+
# Automatically disable ACLs for this bucket going forward
|
|
128
|
+
options.acl = ''
|
|
129
|
+
upload_options.delete(:acl)
|
|
130
|
+
retry
|
|
131
|
+
else
|
|
132
|
+
raise e
|
|
133
|
+
end
|
|
149
134
|
end
|
|
150
|
-
|
|
151
|
-
object.put(upload_options)
|
|
152
135
|
end
|
|
153
136
|
|
|
154
137
|
def ignore!
|
|
@@ -330,6 +313,45 @@ module Middleman
|
|
|
330
313
|
end
|
|
331
314
|
|
|
332
315
|
protected
|
|
316
|
+
|
|
317
|
+
def build_upload_options
|
|
318
|
+
upload_options = {
|
|
319
|
+
body: local_content,
|
|
320
|
+
content_type: content_type
|
|
321
|
+
}
|
|
322
|
+
# Only add ACL if enabled (not for buckets with ACLs disabled)
|
|
323
|
+
upload_options[:acl] = options.acl if options.acl_enabled?
|
|
324
|
+
|
|
325
|
+
# Add metadata if present
|
|
326
|
+
if local_content_md5
|
|
327
|
+
upload_options[:metadata] = { 'content-md5' => local_content_md5 }
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Add redirect if present
|
|
331
|
+
upload_options[:website_redirect_location] = redirect_url if redirect?
|
|
332
|
+
|
|
333
|
+
# Add content encoding if present
|
|
334
|
+
upload_options[:content_encoding] = "gzip" if options.prefer_gzip && gzipped
|
|
335
|
+
|
|
336
|
+
# Add cache control and expires if present
|
|
337
|
+
if caching_policy
|
|
338
|
+
upload_options[:cache_control] = caching_policy.cache_control
|
|
339
|
+
upload_options[:expires] = caching_policy.expires
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# Add storage class if needed
|
|
343
|
+
if options.reduced_redundancy_storage
|
|
344
|
+
upload_options[:storage_class] = 'REDUCED_REDUNDANCY'
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# Add encryption if needed
|
|
348
|
+
if options.encryption
|
|
349
|
+
upload_options[:server_side_encryption] = 'AES256'
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
upload_options
|
|
353
|
+
end
|
|
354
|
+
|
|
333
355
|
def bucket
|
|
334
356
|
Middleman::S3Sync.bucket
|
|
335
357
|
end
|
|
@@ -77,7 +77,28 @@ module Middleman
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def s3_sync_options
|
|
80
|
-
|
|
80
|
+
self
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def acl_enabled?
|
|
84
|
+
# ACLs are disabled if acl is explicitly set to nil, empty string, or false
|
|
85
|
+
acl_value = options.acl
|
|
86
|
+
return false if acl_value.nil? || acl_value == '' || acl_value == false
|
|
87
|
+
# Otherwise ACLs are enabled (using default or explicit value)
|
|
88
|
+
true
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Delegate option readers to the options object
|
|
92
|
+
def method_missing(method, *args, &block)
|
|
93
|
+
if options.respond_to?(method)
|
|
94
|
+
options.send(method, *args, &block)
|
|
95
|
+
else
|
|
96
|
+
super
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def respond_to_missing?(method, include_private = false)
|
|
101
|
+
options.respond_to?(method) || super
|
|
81
102
|
end
|
|
82
103
|
|
|
83
104
|
# Read config options from an IO stream and set them on `self`. Defaults
|
data/middleman-s3_sync.gemspec
CHANGED
|
@@ -20,15 +20,13 @@ Gem::Specification.new do |gem|
|
|
|
20
20
|
|
|
21
21
|
gem.add_runtime_dependency 'middleman-core'
|
|
22
22
|
gem.add_runtime_dependency 'middleman-cli'
|
|
23
|
-
gem.add_runtime_dependency '
|
|
24
|
-
gem.add_runtime_dependency 'aws-sdk-s3'
|
|
23
|
+
gem.add_runtime_dependency 'aws-sdk-s3', '>= 1.187.0'
|
|
25
24
|
gem.add_runtime_dependency 'aws-sdk-cloudfront'
|
|
26
|
-
gem.add_runtime_dependency 'map'
|
|
25
|
+
gem.add_runtime_dependency 'map', '6.6.0'
|
|
27
26
|
gem.add_runtime_dependency 'parallel'
|
|
28
27
|
gem.add_runtime_dependency 'ruby-progressbar'
|
|
29
28
|
gem.add_runtime_dependency 'ansi', '~> 1.5.0'
|
|
30
29
|
gem.add_runtime_dependency 'mime-types', '~> 3.1'
|
|
31
|
-
gem.add_runtime_dependency 'base64'
|
|
32
30
|
gem.add_runtime_dependency 'nokogiri', '>= 1.18.4'
|
|
33
31
|
|
|
34
32
|
gem.add_development_dependency 'rake'
|
|
@@ -152,6 +152,88 @@ describe 'AWS SDK Parameter Validation' do
|
|
|
152
152
|
resource.upload!
|
|
153
153
|
end
|
|
154
154
|
|
|
155
|
+
context 'when ACL is set to empty string (for buckets with ACLs disabled)' do
|
|
156
|
+
before do
|
|
157
|
+
options.acl = ''
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it 'does not include acl parameter in upload' do
|
|
161
|
+
expect(s3_object).to receive(:put) do |upload_options|
|
|
162
|
+
expect(upload_options).not_to have_key(:acl)
|
|
163
|
+
expect(upload_options[:body]).to eq('test content')
|
|
164
|
+
expect(upload_options[:content_type]).to eq('text/html')
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
resource.upload!
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
context 'when ACL is set to nil (for buckets with ACLs disabled)' do
|
|
172
|
+
before do
|
|
173
|
+
options.acl = nil
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it 'does not include acl parameter in upload' do
|
|
177
|
+
expect(s3_object).to receive(:put) do |upload_options|
|
|
178
|
+
expect(upload_options).not_to have_key(:acl)
|
|
179
|
+
expect(upload_options[:body]).to eq('test content')
|
|
180
|
+
expect(upload_options[:content_type]).to eq('text/html')
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
resource.upload!
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
context 'when bucket does not support ACLs (auto-detection)' do
|
|
188
|
+
before do
|
|
189
|
+
# ACL is enabled by default
|
|
190
|
+
expect(options.acl).to eq('public-read')
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it 'automatically retries without ACL when AccessControlListNotSupported error occurs' do
|
|
194
|
+
call_count = 0
|
|
195
|
+
expect(s3_object).to receive(:put).twice do |upload_options|
|
|
196
|
+
call_count += 1
|
|
197
|
+
if call_count == 1
|
|
198
|
+
# First call should include ACL
|
|
199
|
+
expect(upload_options[:acl]).to eq('public-read')
|
|
200
|
+
raise Aws::S3::Errors::AccessControlListNotSupported.new(nil, 'The bucket does not allow ACLs')
|
|
201
|
+
else
|
|
202
|
+
# Second call should not include ACL
|
|
203
|
+
expect(upload_options).not_to have_key(:acl)
|
|
204
|
+
expect(upload_options[:body]).to eq('test content')
|
|
205
|
+
expect(upload_options[:content_type]).to eq('text/html')
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Should automatically disable ACLs after the error
|
|
210
|
+
resource.upload!
|
|
211
|
+
expect(options.acl_enabled?).to be false
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
it 'permanently disables ACLs after detecting bucket does not support them' do
|
|
215
|
+
call_count = 0
|
|
216
|
+
allow(s3_object).to receive(:put) do |upload_options|
|
|
217
|
+
call_count += 1
|
|
218
|
+
if call_count == 1
|
|
219
|
+
expect(upload_options[:acl]).to eq('public-read')
|
|
220
|
+
raise Aws::S3::Errors::AccessControlListNotSupported.new(nil, 'The bucket does not allow ACLs')
|
|
221
|
+
else
|
|
222
|
+
# Second call should succeed without ACL
|
|
223
|
+
expect(upload_options).not_to have_key(:acl)
|
|
224
|
+
true
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
resource.upload!
|
|
229
|
+
expect(options.acl_enabled?).to be false
|
|
230
|
+
|
|
231
|
+
# Verify ACLs stay disabled for future uploads
|
|
232
|
+
resource.upload!
|
|
233
|
+
expect(call_count).to eq(3) # First attempt, retry, and third upload
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
155
237
|
context 'when gzip is enabled' do
|
|
156
238
|
before do
|
|
157
239
|
options.prefer_gzip = true
|
|
@@ -306,6 +388,34 @@ describe 'AWS SDK Parameter Validation' do
|
|
|
306
388
|
expect(attributes).not_to have_key('x-amz-meta-content-md5')
|
|
307
389
|
end
|
|
308
390
|
|
|
391
|
+
context 'when ACL is set to empty string' do
|
|
392
|
+
before do
|
|
393
|
+
options.acl = ''
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
it 'does not include acl in attributes' do
|
|
397
|
+
attributes = resource.to_h
|
|
398
|
+
|
|
399
|
+
expect(attributes).not_to have_key(:acl)
|
|
400
|
+
expect(attributes[:key]).to eq('test/file.html')
|
|
401
|
+
expect(attributes[:content_type]).to eq('text/html')
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
context 'when ACL is set to nil' do
|
|
406
|
+
before do
|
|
407
|
+
options.acl = nil
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
it 'does not include acl in attributes' do
|
|
411
|
+
attributes = resource.to_h
|
|
412
|
+
|
|
413
|
+
expect(attributes).not_to have_key(:acl)
|
|
414
|
+
expect(attributes[:key]).to eq('test/file.html')
|
|
415
|
+
expect(attributes[:content_type]).to eq('text/html')
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
|
|
309
419
|
context 'when resource has a redirect' do
|
|
310
420
|
let(:mm_resource) do
|
|
311
421
|
double(
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Middleman::S3SyncExtension do
|
|
4
|
+
let(:app) { double('app').as_null_object }
|
|
5
|
+
|
|
6
|
+
let(:extension) { described_class.new(app, {}) }
|
|
7
|
+
|
|
8
|
+
describe '#acl_enabled?' do
|
|
9
|
+
context 'when acl has default value' do
|
|
10
|
+
it 'returns true' do
|
|
11
|
+
expect(extension.acl_enabled?).to be true
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
context 'when acl is explicitly set to a value' do
|
|
16
|
+
let(:extension) { described_class.new(app, acl: 'private') }
|
|
17
|
+
|
|
18
|
+
it 'returns true' do
|
|
19
|
+
expect(extension.acl_enabled?).to be true
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
context 'when acl is set to nil' do
|
|
24
|
+
let(:extension) { described_class.new(app, acl: nil) }
|
|
25
|
+
|
|
26
|
+
it 'returns false' do
|
|
27
|
+
expect(extension.acl_enabled?).to be false
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
context 'when acl is set to empty string' do
|
|
32
|
+
let(:extension) { described_class.new(app, acl: '') }
|
|
33
|
+
|
|
34
|
+
it 'returns false' do
|
|
35
|
+
expect(extension.acl_enabled?).to be false
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
context 'when acl is set to false' do
|
|
40
|
+
let(:extension) { described_class.new(app, acl: false) }
|
|
41
|
+
|
|
42
|
+
it 'returns false' do
|
|
43
|
+
expect(extension.acl_enabled?).to be false
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
describe '#s3_sync_options' do
|
|
49
|
+
it 'returns the extension instance itself' do
|
|
50
|
+
expect(extension.s3_sync_options).to eq(extension)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'allows access to acl_enabled? through s3_sync_options' do
|
|
54
|
+
expect(extension.s3_sync_options.acl_enabled?).to be true
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe 'option delegation' do
|
|
59
|
+
let(:extension) do
|
|
60
|
+
described_class.new(app,
|
|
61
|
+
bucket: 'test-bucket',
|
|
62
|
+
region: 'us-west-2',
|
|
63
|
+
acl: 'public-read',
|
|
64
|
+
verbose: true
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'delegates option readers to the options object' do
|
|
69
|
+
expect(extension.bucket).to eq('test-bucket')
|
|
70
|
+
expect(extension.region).to eq('us-west-2')
|
|
71
|
+
expect(extension.acl).to eq('public-read')
|
|
72
|
+
expect(extension.verbose).to be true
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it 'responds to option methods' do
|
|
76
|
+
expect(extension.respond_to?(:bucket)).to be true
|
|
77
|
+
expect(extension.respond_to?(:region)).to be true
|
|
78
|
+
expect(extension.respond_to?(:acl)).to be true
|
|
79
|
+
expect(extension.respond_to?(:verbose)).to be true
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'does not respond to non-existent methods' do
|
|
83
|
+
expect(extension.respond_to?(:nonexistent_method)).to be false
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it 'raises NoMethodError for non-existent methods' do
|
|
87
|
+
expect { extension.nonexistent_method }.to raise_error(NoMethodError)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe 'integration with S3Sync module' do
|
|
92
|
+
before do
|
|
93
|
+
allow(Middleman::Application).to receive(:root).and_return('/tmp')
|
|
94
|
+
allow(File).to receive(:exist?).with('/tmp/.s3_sync').and_return(false)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
let(:extension) do
|
|
98
|
+
described_class.new(app,
|
|
99
|
+
bucket: 'test-bucket',
|
|
100
|
+
acl: 'private'
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it 'sets s3_sync_options to the extension instance' do
|
|
105
|
+
extension.after_configuration
|
|
106
|
+
expect(Middleman::S3Sync.s3_sync_options).to eq(extension)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'allows S3Sync to access acl_enabled? through s3_sync_options' do
|
|
110
|
+
extension.after_configuration
|
|
111
|
+
expect(Middleman::S3Sync.s3_sync_options.acl_enabled?).to be true
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Middleman::S3Sync::Options do
|
|
4
|
+
let(:options) { described_class.new }
|
|
5
|
+
|
|
6
|
+
describe 'option accessors' do
|
|
7
|
+
it 'provides accessors for all defined options' do
|
|
8
|
+
described_class::OPTIONS.each do |option_name|
|
|
9
|
+
expect(options).to respond_to(option_name)
|
|
10
|
+
expect(options).to respond_to("#{option_name}=")
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe 'AWS credentials options' do
|
|
16
|
+
it 'supports aws_access_key_id' do
|
|
17
|
+
options.aws_access_key_id = 'test_key'
|
|
18
|
+
expect(options.aws_access_key_id).to eq('test_key')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'supports aws_secret_access_key' do
|
|
22
|
+
options.aws_secret_access_key = 'test_secret'
|
|
23
|
+
expect(options.aws_secret_access_key).to eq('test_secret')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'supports aws_session_token' do
|
|
27
|
+
options.aws_session_token = 'test_token'
|
|
28
|
+
expect(options.aws_session_token).to eq('test_token')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'falls back to ENV for aws_access_key_id' do
|
|
32
|
+
ENV['AWS_ACCESS_KEY_ID'] = 'env_key'
|
|
33
|
+
expect(options.aws_access_key_id).to eq('env_key')
|
|
34
|
+
ensure
|
|
35
|
+
ENV.delete('AWS_ACCESS_KEY_ID')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'falls back to ENV for aws_secret_access_key' do
|
|
39
|
+
ENV['AWS_SECRET_ACCESS_KEY'] = 'env_secret'
|
|
40
|
+
expect(options.aws_secret_access_key).to eq('env_secret')
|
|
41
|
+
ensure
|
|
42
|
+
ENV.delete('AWS_SECRET_ACCESS_KEY')
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe 'CloudFront options' do
|
|
47
|
+
it 'supports cloudfront_distribution_id' do
|
|
48
|
+
options.cloudfront_distribution_id = 'E1234567890123'
|
|
49
|
+
expect(options.cloudfront_distribution_id).to eq('E1234567890123')
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it 'supports cloudfront_invalidate' do
|
|
53
|
+
options.cloudfront_invalidate = true
|
|
54
|
+
expect(options.cloudfront_invalidate).to be true
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'supports cloudfront_invalidate_all' do
|
|
58
|
+
options.cloudfront_invalidate_all = true
|
|
59
|
+
expect(options.cloudfront_invalidate_all).to be true
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'supports cloudfront_invalidation_batch_size' do
|
|
63
|
+
options.cloudfront_invalidation_batch_size = 500
|
|
64
|
+
expect(options.cloudfront_invalidation_batch_size).to eq(500)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'supports cloudfront_invalidation_max_retries' do
|
|
68
|
+
options.cloudfront_invalidation_max_retries = 10
|
|
69
|
+
expect(options.cloudfront_invalidation_max_retries).to eq(10)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'supports cloudfront_invalidation_batch_delay' do
|
|
73
|
+
options.cloudfront_invalidation_batch_delay = 5
|
|
74
|
+
expect(options.cloudfront_invalidation_batch_delay).to eq(5)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'supports cloudfront_wait' do
|
|
78
|
+
options.cloudfront_wait = true
|
|
79
|
+
expect(options.cloudfront_wait).to be true
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe 'S3 options' do
|
|
84
|
+
it 'supports bucket' do
|
|
85
|
+
options.bucket = 'my-bucket'
|
|
86
|
+
expect(options.bucket).to eq('my-bucket')
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'supports region' do
|
|
90
|
+
options.region = 'us-west-2'
|
|
91
|
+
expect(options.region).to eq('us-west-2')
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it 'supports endpoint' do
|
|
95
|
+
options.endpoint = 'https://s3-compatible.example.com'
|
|
96
|
+
expect(options.endpoint).to eq('https://s3-compatible.example.com')
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it 'supports prefix' do
|
|
100
|
+
options.prefix = 'my-prefix'
|
|
101
|
+
expect(options.prefix).to eq('my-prefix/')
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it 'supports path_style' do
|
|
105
|
+
options.path_style = false
|
|
106
|
+
expect(options.path_style).to be false
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'defaults path_style to true' do
|
|
110
|
+
expect(options.path_style).to be true
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it 'supports encryption' do
|
|
114
|
+
options.encryption = true
|
|
115
|
+
expect(options.encryption).to be true
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'defaults encryption to false' do
|
|
119
|
+
expect(options.encryption).to be false
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'supports reduced_redundancy_storage' do
|
|
123
|
+
options.reduced_redundancy_storage = true
|
|
124
|
+
expect(options.reduced_redundancy_storage).to be true
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
describe 'ACL options' do
|
|
129
|
+
it 'supports acl' do
|
|
130
|
+
options.acl = 'private'
|
|
131
|
+
expect(options.acl).to eq('private')
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it 'defaults acl to public-read' do
|
|
135
|
+
expect(options.acl).to eq('public-read')
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'supports acl_enabled?' do
|
|
139
|
+
expect(options.acl_enabled?).to be true
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
context 'when acl is disabled' do
|
|
143
|
+
it 'returns false for acl_enabled? when set to empty string' do
|
|
144
|
+
options.acl = ''
|
|
145
|
+
expect(options.acl_enabled?).to be false
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it 'returns false for acl_enabled? when set to nil' do
|
|
149
|
+
options.acl = nil
|
|
150
|
+
expect(options.acl_enabled?).to be false
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it 'returns false for acl_enabled? when set to false' do
|
|
154
|
+
options.acl = false
|
|
155
|
+
expect(options.acl_enabled?).to be false
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
describe 'sync behavior options' do
|
|
161
|
+
it 'supports delete' do
|
|
162
|
+
options.delete = false
|
|
163
|
+
expect(options.delete).to be false
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it 'defaults delete to true' do
|
|
167
|
+
expect(options.delete).to be true
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it 'supports force' do
|
|
171
|
+
options.force = true
|
|
172
|
+
expect(options.force).to be true
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it 'supports prefer_gzip' do
|
|
176
|
+
options.prefer_gzip = false
|
|
177
|
+
expect(options.prefer_gzip).to be false
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it 'defaults prefer_gzip to true' do
|
|
181
|
+
expect(options.prefer_gzip).to be true
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
it 'supports verbose' do
|
|
185
|
+
options.verbose = true
|
|
186
|
+
expect(options.verbose).to be true
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it 'supports dry_run' do
|
|
190
|
+
options.dry_run = true
|
|
191
|
+
expect(options.dry_run).to be true
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it 'supports version_bucket' do
|
|
195
|
+
options.version_bucket = true
|
|
196
|
+
expect(options.version_bucket).to be true
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it 'defaults version_bucket to false' do
|
|
200
|
+
expect(options.version_bucket).to be false
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
describe 'build options' do
|
|
205
|
+
it 'supports build_dir' do
|
|
206
|
+
options.build_dir = 'dist'
|
|
207
|
+
expect(options.build_dir).to eq('dist')
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
it 'supports after_build' do
|
|
211
|
+
options.after_build = true
|
|
212
|
+
expect(options.after_build).to be true
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
it 'defaults after_build to false' do
|
|
216
|
+
expect(options.after_build).to be false
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
describe 'content options' do
|
|
221
|
+
it 'supports content_types' do
|
|
222
|
+
content_types = { '.webp' => 'image/webp' }
|
|
223
|
+
options.content_types = content_types
|
|
224
|
+
expect(options.content_types).to eq(content_types)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
it 'supports ignore_paths' do
|
|
228
|
+
ignore_paths = [/\.bak$/, 'temp/']
|
|
229
|
+
options.ignore_paths = ignore_paths
|
|
230
|
+
expect(options.ignore_paths).to eq(ignore_paths)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
it 'defaults ignore_paths to empty array' do
|
|
234
|
+
expect(options.ignore_paths).to eq([])
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
describe 'website options' do
|
|
239
|
+
it 'supports index_document' do
|
|
240
|
+
options.index_document = 'index.html'
|
|
241
|
+
expect(options.index_document).to eq('index.html')
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
it 'supports error_document' do
|
|
245
|
+
options.error_document = '404.html'
|
|
246
|
+
expect(options.error_document).to eq('404.html')
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
describe 'option consistency' do
|
|
251
|
+
it 'includes all options from extension in OPTIONS constant' do
|
|
252
|
+
# These are the options defined in the extension
|
|
253
|
+
extension_options = [
|
|
254
|
+
:prefix, :http_prefix, :acl, :bucket, :endpoint, :region,
|
|
255
|
+
:aws_access_key_id, :aws_secret_access_key, :aws_session_token,
|
|
256
|
+
:after_build, :build_dir, :delete, :encryption, :force,
|
|
257
|
+
:prefer_gzip, :reduced_redundancy_storage, :path_style,
|
|
258
|
+
:version_bucket, :verbose, :dry_run, :index_document,
|
|
259
|
+
:error_document, :content_types, :ignore_paths,
|
|
260
|
+
:cloudfront_distribution_id, :cloudfront_invalidate,
|
|
261
|
+
:cloudfront_invalidate_all, :cloudfront_invalidation_batch_size,
|
|
262
|
+
:cloudfront_invalidation_max_retries, :cloudfront_invalidation_batch_delay,
|
|
263
|
+
:cloudfront_wait
|
|
264
|
+
]
|
|
265
|
+
|
|
266
|
+
extension_options.each do |option_name|
|
|
267
|
+
expect(described_class::OPTIONS).to include(option_name),
|
|
268
|
+
"Expected OPTIONS to include :#{option_name}"
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: middleman-s3_sync
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.6.
|
|
4
|
+
version: 4.6.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Frederic Jean
|
|
8
8
|
- Will Koehler
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: middleman-core
|
|
@@ -38,34 +38,20 @@ dependencies:
|
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '0'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: unf
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - ">="
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0'
|
|
48
|
-
type: :runtime
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - ">="
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '0'
|
|
55
41
|
- !ruby/object:Gem::Dependency
|
|
56
42
|
name: aws-sdk-s3
|
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
|
58
44
|
requirements:
|
|
59
45
|
- - ">="
|
|
60
46
|
- !ruby/object:Gem::Version
|
|
61
|
-
version:
|
|
47
|
+
version: 1.187.0
|
|
62
48
|
type: :runtime
|
|
63
49
|
prerelease: false
|
|
64
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
51
|
requirements:
|
|
66
52
|
- - ">="
|
|
67
53
|
- !ruby/object:Gem::Version
|
|
68
|
-
version:
|
|
54
|
+
version: 1.187.0
|
|
69
55
|
- !ruby/object:Gem::Dependency
|
|
70
56
|
name: aws-sdk-cloudfront
|
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -84,16 +70,16 @@ dependencies:
|
|
|
84
70
|
name: map
|
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
|
86
72
|
requirements:
|
|
87
|
-
- -
|
|
73
|
+
- - '='
|
|
88
74
|
- !ruby/object:Gem::Version
|
|
89
|
-
version:
|
|
75
|
+
version: 6.6.0
|
|
90
76
|
type: :runtime
|
|
91
77
|
prerelease: false
|
|
92
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
79
|
requirements:
|
|
94
|
-
- -
|
|
80
|
+
- - '='
|
|
95
81
|
- !ruby/object:Gem::Version
|
|
96
|
-
version:
|
|
82
|
+
version: 6.6.0
|
|
97
83
|
- !ruby/object:Gem::Dependency
|
|
98
84
|
name: parallel
|
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -150,20 +136,6 @@ dependencies:
|
|
|
150
136
|
- - "~>"
|
|
151
137
|
- !ruby/object:Gem::Version
|
|
152
138
|
version: '3.1'
|
|
153
|
-
- !ruby/object:Gem::Dependency
|
|
154
|
-
name: base64
|
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
|
156
|
-
requirements:
|
|
157
|
-
- - ">="
|
|
158
|
-
- !ruby/object:Gem::Version
|
|
159
|
-
version: '0'
|
|
160
|
-
type: :runtime
|
|
161
|
-
prerelease: false
|
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
-
requirements:
|
|
164
|
-
- - ">="
|
|
165
|
-
- !ruby/object:Gem::Version
|
|
166
|
-
version: '0'
|
|
167
139
|
- !ruby/object:Gem::Dependency
|
|
168
140
|
name: nokogiri
|
|
169
141
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -311,8 +283,9 @@ executables: []
|
|
|
311
283
|
extensions: []
|
|
312
284
|
extra_rdoc_files: []
|
|
313
285
|
files:
|
|
314
|
-
- ".envrc"
|
|
286
|
+
- ".envrc.backup"
|
|
315
287
|
- ".gitignore"
|
|
288
|
+
- ".mise.toml"
|
|
316
289
|
- ".rspec"
|
|
317
290
|
- ".s3_sync.sample"
|
|
318
291
|
- ".travis.yml"
|
|
@@ -338,6 +311,8 @@ files:
|
|
|
338
311
|
- spec/aws_sdk_parameters_spec.rb
|
|
339
312
|
- spec/caching_policy_spec.rb
|
|
340
313
|
- spec/cloudfront_spec.rb
|
|
314
|
+
- spec/extension_spec.rb
|
|
315
|
+
- spec/options_spec.rb
|
|
341
316
|
- spec/resource_spec.rb
|
|
342
317
|
- spec/s3_sync_integration_spec.rb
|
|
343
318
|
- spec/spec_helper.rb
|
|
@@ -366,6 +341,8 @@ test_files:
|
|
|
366
341
|
- spec/aws_sdk_parameters_spec.rb
|
|
367
342
|
- spec/caching_policy_spec.rb
|
|
368
343
|
- spec/cloudfront_spec.rb
|
|
344
|
+
- spec/extension_spec.rb
|
|
345
|
+
- spec/options_spec.rb
|
|
369
346
|
- spec/resource_spec.rb
|
|
370
347
|
- spec/s3_sync_integration_spec.rb
|
|
371
348
|
- spec/spec_helper.rb
|
/data/{.envrc → .envrc.backup}
RENAMED
|
File without changes
|