asset_sync 2.4.0 → 2.14.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.
- checksums.yaml +4 -4
- data/.github/workflows/tests.yaml +64 -0
- data/.travis.yml +25 -26
- data/Appraisals +6 -10
- data/CHANGELOG.md +158 -2
- data/README.md +120 -9
- data/asset_sync.gemspec +4 -3
- data/gemfiles/{rails_4_1.gemfile → rails_5_2.gemfile} +1 -1
- data/gemfiles/{rails_4_2.gemfile → rails_6_0.gemfile} +1 -1
- data/gemfiles/{rails_5_0.gemfile → rails_6_1.gemfile} +1 -1
- data/lib/asset_sync/config.rb +150 -12
- data/lib/asset_sync/engine.rb +8 -0
- data/lib/asset_sync/multi_mime.rb +7 -2
- data/lib/asset_sync/storage.rb +81 -20
- data/lib/asset_sync/version.rb +1 -1
- data/lib/generators/asset_sync/install_generator.rb +21 -1
- data/lib/generators/asset_sync/templates/asset_sync.rb +21 -0
- data/lib/generators/asset_sync/templates/asset_sync.yml +20 -1
- data/lib/tasks/asset_sync.rake +3 -1
- data/spec/fixtures/backblaze_with_yml/config/asset_sync.yml +20 -0
- data/spec/fixtures/google_with_service_account_yml/config/asset_sync.yml +19 -0
- data/spec/integration/backblaze_intergration_spec.rb +74 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/unit/asset_sync_spec.rb +35 -3
- data/spec/unit/backblaze_spec.rb +150 -0
- data/spec/unit/google_spec.rb +124 -29
- data/spec/unit/multi_mime_spec.rb +47 -0
- data/spec/unit/railsless_spec.rb +4 -3
- data/spec/unit/storage_spec.rb +150 -7
- metadata +49 -14
- data/gemfiles/rails_5_1.gemfile +0 -10
data/asset_sync.gemspec
CHANGED
@@ -15,8 +15,6 @@ Gem::Specification.new do |s|
|
|
15
15
|
|
16
16
|
s.license = 'MIT'
|
17
17
|
|
18
|
-
s.rubyforge_project = "asset_sync"
|
19
|
-
|
20
18
|
s.add_dependency("fog-core")
|
21
19
|
s.add_dependency('unf')
|
22
20
|
s.add_dependency('activemodel', ">= 4.1.0")
|
@@ -24,10 +22,13 @@ Gem::Specification.new do |s|
|
|
24
22
|
|
25
23
|
s.add_development_dependency "rspec"
|
26
24
|
s.add_development_dependency "bundler"
|
27
|
-
s.add_development_dependency "
|
25
|
+
s.add_development_dependency "coveralls", ">= 0.7"
|
26
|
+
|
27
|
+
s.add_development_dependency('mime-types', ">= 3.0")
|
28
28
|
|
29
29
|
s.add_development_dependency "fog-aws"
|
30
30
|
s.add_development_dependency "fog-azure-rm"
|
31
|
+
s.add_development_dependency "fog-backblaze"
|
31
32
|
|
32
33
|
s.add_development_dependency "uglifier"
|
33
34
|
s.add_development_dependency "appraisal"
|
@@ -5,6 +5,6 @@ source "https://rubygems.org"
|
|
5
5
|
gem "rcov", platforms: :mri_18, group: [:development, :test]
|
6
6
|
gem "simplecov", platforms: [:jruby, :mri_19, :ruby_19, :mri_20, :rbx], group: [:development, :test], require: false
|
7
7
|
gem "jruby-openssl", platform: :jruby
|
8
|
-
gem "rails", "~>
|
8
|
+
gem "rails", "~> 5.2.0"
|
9
9
|
|
10
10
|
gemspec path: "../"
|
@@ -5,6 +5,6 @@ source "https://rubygems.org"
|
|
5
5
|
gem "rcov", platforms: :mri_18, group: [:development, :test]
|
6
6
|
gem "simplecov", platforms: [:jruby, :mri_19, :ruby_19, :mri_20, :rbx], group: [:development, :test], require: false
|
7
7
|
gem "jruby-openssl", platform: :jruby
|
8
|
-
gem "rails", "~>
|
8
|
+
gem "rails", "~> 6.0.0"
|
9
9
|
|
10
10
|
gemspec path: "../"
|
@@ -5,6 +5,6 @@ source "https://rubygems.org"
|
|
5
5
|
gem "rcov", platforms: :mri_18, group: [:development, :test]
|
6
6
|
gem "simplecov", platforms: [:jruby, :mri_19, :ruby_19, :mri_20, :rbx], group: [:development, :test], require: false
|
7
7
|
gem "jruby-openssl", platform: :jruby
|
8
|
-
gem "rails", "~>
|
8
|
+
gem "rails", "~> 6.1.0"
|
9
9
|
|
10
10
|
gemspec path: "../"
|
data/lib/asset_sync/config.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_model"
|
2
4
|
require "erb"
|
3
5
|
require "yaml"
|
@@ -17,7 +19,6 @@ module AssetSync
|
|
17
19
|
attr_accessor :always_upload
|
18
20
|
attr_accessor :ignored_files
|
19
21
|
attr_accessor :prefix
|
20
|
-
attr_accessor :public_path
|
21
22
|
attr_accessor :enabled
|
22
23
|
attr_accessor :custom_headers
|
23
24
|
attr_accessor :run_on_precompile
|
@@ -25,15 +26,20 @@ module AssetSync
|
|
25
26
|
attr_accessor :cdn_distribution_id
|
26
27
|
attr_accessor :cache_asset_regexps
|
27
28
|
attr_accessor :include_manifest
|
29
|
+
attr_accessor :concurrent_uploads
|
30
|
+
attr_accessor :concurrent_uploads_max_threads
|
31
|
+
attr_accessor :remote_file_list_cache_file_path
|
28
32
|
|
29
33
|
# FOG configuration
|
30
34
|
attr_accessor :fog_provider # Currently Supported ['AWS', 'Rackspace']
|
31
35
|
attr_accessor :fog_directory # e.g. 'the-bucket-name'
|
32
36
|
attr_accessor :fog_region # e.g. 'eu-west-1'
|
37
|
+
attr_reader :fog_public # e.g. true, false, "default"
|
33
38
|
|
34
39
|
# Amazon AWS
|
35
|
-
attr_accessor :aws_access_key_id, :aws_secret_access_key, :aws_reduced_redundancy, :aws_iam_roles, :aws_signature_version
|
40
|
+
attr_accessor :aws_access_key_id, :aws_secret_access_key, :aws_session_token, :aws_reduced_redundancy, :aws_iam_roles, :aws_signature_version
|
36
41
|
attr_accessor :fog_host # e.g. 's3.amazonaws.com'
|
42
|
+
attr_accessor :fog_port # e.g. '9000'
|
37
43
|
attr_accessor :fog_path_style # e.g. true
|
38
44
|
attr_accessor :fog_scheme # e.g. 'http'
|
39
45
|
|
@@ -41,12 +47,20 @@ module AssetSync
|
|
41
47
|
attr_accessor :rackspace_username, :rackspace_api_key, :rackspace_auth_url
|
42
48
|
|
43
49
|
# Google Storage
|
44
|
-
attr_accessor :google_storage_secret_access_key, :google_storage_access_key_id
|
50
|
+
attr_accessor :google_storage_secret_access_key, :google_storage_access_key_id # when using S3 interop
|
51
|
+
attr_accessor :google_json_key_location # when using service accounts
|
52
|
+
attr_accessor :google_json_key_string # when using service accounts
|
53
|
+
attr_accessor :google_project # when using service accounts
|
45
54
|
|
46
55
|
# Azure Blob with Fog::AzureRM
|
47
56
|
attr_accessor :azure_storage_account_name
|
48
57
|
attr_accessor :azure_storage_access_key
|
49
58
|
|
59
|
+
# Backblaze B2 with Fog::Backblaze
|
60
|
+
attr_accessor :b2_key_id
|
61
|
+
attr_accessor :b2_key_token
|
62
|
+
attr_accessor :b2_bucket_id
|
63
|
+
|
50
64
|
validates :existing_remote_files, :inclusion => { :in => %w(keep delete ignore) }
|
51
65
|
|
52
66
|
validates :fog_provider, :presence => true
|
@@ -56,11 +70,19 @@ module AssetSync
|
|
56
70
|
validates :aws_secret_access_key, :presence => true, :if => proc {aws? && !aws_iam?}
|
57
71
|
validates :rackspace_username, :presence => true, :if => :rackspace?
|
58
72
|
validates :rackspace_api_key, :presence => true, :if => :rackspace?
|
59
|
-
validates :google_storage_secret_access_key, :presence => true, :if => :
|
60
|
-
validates :google_storage_access_key_id, :presence => true, :if => :
|
73
|
+
validates :google_storage_secret_access_key, :presence => true, :if => :google_interop?
|
74
|
+
validates :google_storage_access_key_id, :presence => true, :if => :google_interop?
|
75
|
+
validates :google_project, :presence => true, :if => :google_service_account?
|
76
|
+
validate(:if => :google_service_account?) do
|
77
|
+
unless google_json_key_location.present? || google_json_key_string.present?
|
78
|
+
errors.add(:base, 'must provide either google_json_key_location or google_json_key_string if using Google service account')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
validates :concurrent_uploads, :inclusion => { :in => [true, false] }
|
61
82
|
|
62
83
|
def initialize
|
63
84
|
self.fog_region = nil
|
85
|
+
self.fog_public = true
|
64
86
|
self.existing_remote_files = 'keep'
|
65
87
|
self.gzip_compression = false
|
66
88
|
self.manifest = false
|
@@ -75,6 +97,9 @@ module AssetSync
|
|
75
97
|
self.invalidate = []
|
76
98
|
self.cache_asset_regexps = []
|
77
99
|
self.include_manifest = false
|
100
|
+
self.concurrent_uploads = false
|
101
|
+
self.concurrent_uploads_max_threads = 10
|
102
|
+
self.remote_file_list_cache_file_path = nil
|
78
103
|
@additional_local_file_paths_procs = []
|
79
104
|
|
80
105
|
load_yml! if defined?(::Rails) && yml_exists?
|
@@ -126,10 +151,22 @@ module AssetSync
|
|
126
151
|
fog_provider =~ /google/i
|
127
152
|
end
|
128
153
|
|
154
|
+
def google_interop?
|
155
|
+
google? && google_json_key_location.nil? && google_json_key_string.nil?
|
156
|
+
end
|
157
|
+
|
158
|
+
def google_service_account?
|
159
|
+
google? && (google_json_key_location || google_json_key_string)
|
160
|
+
end
|
161
|
+
|
129
162
|
def azure_rm?
|
130
163
|
fog_provider =~ /azurerm/i
|
131
164
|
end
|
132
165
|
|
166
|
+
def backblaze?
|
167
|
+
fog_provider =~ /backblaze/i
|
168
|
+
end
|
169
|
+
|
133
170
|
def cache_asset_regexp=(cache_asset_regexp)
|
134
171
|
self.cache_asset_regexps = [cache_asset_regexp]
|
135
172
|
end
|
@@ -155,24 +192,42 @@ module AssetSync
|
|
155
192
|
@public_path || ::Rails.public_path
|
156
193
|
end
|
157
194
|
|
195
|
+
def public_path=(path)
|
196
|
+
# Generate absolute path even when relative path passed in
|
197
|
+
# Required for generating relative sprockets manifest path
|
198
|
+
pathname = Pathname(path)
|
199
|
+
@public_path = if pathname.absolute?
|
200
|
+
pathname
|
201
|
+
elsif defined?(::Rails.root)
|
202
|
+
::Rails.root.join(pathname)
|
203
|
+
else
|
204
|
+
Pathname(::Dir.pwd).join(pathname)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
158
208
|
def load_yml!
|
159
209
|
self.enabled = yml["enabled"] if yml.has_key?('enabled')
|
160
210
|
self.fog_provider = yml["fog_provider"]
|
161
211
|
self.fog_host = yml["fog_host"]
|
212
|
+
self.fog_port = yml["fog_port"]
|
162
213
|
self.fog_directory = yml["fog_directory"]
|
163
214
|
self.fog_region = yml["fog_region"]
|
215
|
+
self.fog_public = yml["fog_public"] if yml.has_key?("fog_public")
|
164
216
|
self.fog_path_style = yml["fog_path_style"]
|
165
217
|
self.fog_scheme = yml["fog_scheme"]
|
166
218
|
self.aws_access_key_id = yml["aws_access_key_id"]
|
167
219
|
self.aws_secret_access_key = yml["aws_secret_access_key"]
|
220
|
+
self.aws_session_token = yml["aws_session_token"] if yml.has_key?("aws_session_token")
|
168
221
|
self.aws_reduced_redundancy = yml["aws_reduced_redundancy"]
|
169
222
|
self.aws_iam_roles = yml["aws_iam_roles"]
|
170
223
|
self.aws_signature_version = yml["aws_signature_version"]
|
171
224
|
self.rackspace_username = yml["rackspace_username"]
|
172
225
|
self.rackspace_auth_url = yml["rackspace_auth_url"] if yml.has_key?("rackspace_auth_url")
|
173
226
|
self.rackspace_api_key = yml["rackspace_api_key"]
|
174
|
-
self.
|
175
|
-
self.
|
227
|
+
self.google_json_key_location = yml["google_json_key_location"] if yml.has_key?("google_json_key_location")
|
228
|
+
self.google_project = yml["google_project"] if yml.has_key?("google_project")
|
229
|
+
self.google_storage_secret_access_key = yml["google_storage_secret_access_key"] if yml.has_key?("google_storage_secret_access_key")
|
230
|
+
self.google_storage_access_key_id = yml["google_storage_access_key_id"] if yml.has_key?("google_storage_access_key_id")
|
176
231
|
self.existing_remote_files = yml["existing_remote_files"] if yml.has_key?("existing_remote_files")
|
177
232
|
self.gzip_compression = yml["gzip_compression"] if yml.has_key?("gzip_compression")
|
178
233
|
self.manifest = yml["manifest"] if yml.has_key?("manifest")
|
@@ -185,10 +240,17 @@ module AssetSync
|
|
185
240
|
self.cdn_distribution_id = yml['cdn_distribution_id'] if yml.has_key?("cdn_distribution_id")
|
186
241
|
self.cache_asset_regexps = yml['cache_asset_regexps'] if yml.has_key?("cache_asset_regexps")
|
187
242
|
self.include_manifest = yml['include_manifest'] if yml.has_key?("include_manifest")
|
243
|
+
self.concurrent_uploads = yml['concurrent_uploads'] if yml.has_key?('concurrent_uploads')
|
244
|
+
self.concurrent_uploads_max_threads = yml['concurrent_uploads_max_threads'] if yml.has_key?('concurrent_uploads_max_threads')
|
245
|
+
self.remote_file_list_cache_file_path = yml['remote_file_list_cache_file_path'] if yml.has_key?('remote_file_list_cache_file_path')
|
188
246
|
|
189
247
|
self.azure_storage_account_name = yml['azure_storage_account_name'] if yml.has_key?("azure_storage_account_name")
|
190
248
|
self.azure_storage_access_key = yml['azure_storage_access_key'] if yml.has_key?("azure_storage_access_key")
|
191
249
|
|
250
|
+
self.b2_key_id = yml['b2_key_id'] if yml.has_key?("b2_key_id")
|
251
|
+
self.b2_key_token = yml['b2_key_token'] if yml.has_key?("b2_key_token")
|
252
|
+
self.b2_bucket_id = yml['b2_bucket_id'] if yml.has_key?("b2_bucket_id")
|
253
|
+
|
192
254
|
# TODO deprecate the other old style config settings. FML.
|
193
255
|
self.aws_access_key_id = yml["aws_access_key"] if yml.has_key?("aws_access_key")
|
194
256
|
self.aws_secret_access_key = yml["aws_access_secret"] if yml.has_key?("aws_access_secret")
|
@@ -217,8 +279,10 @@ module AssetSync
|
|
217
279
|
:aws_access_key_id => aws_access_key_id,
|
218
280
|
:aws_secret_access_key => aws_secret_access_key
|
219
281
|
})
|
282
|
+
options.merge!({:aws_session_token => aws_session_token}) if aws_session_token
|
220
283
|
end
|
221
284
|
options.merge!({:host => fog_host}) if fog_host
|
285
|
+
options.merge!({:port => fog_port}) if fog_port
|
222
286
|
options.merge!({:scheme => fog_scheme}) if fog_scheme
|
223
287
|
options.merge!({:aws_signature_version => aws_signature_version}) if aws_signature_version
|
224
288
|
options.merge!({:path_style => fog_path_style}) if fog_path_style
|
@@ -231,10 +295,16 @@ module AssetSync
|
|
231
295
|
options.merge!({ :rackspace_region => fog_region }) if fog_region
|
232
296
|
options.merge!({ :rackspace_auth_url => rackspace_auth_url }) if rackspace_auth_url
|
233
297
|
elsif google?
|
234
|
-
|
235
|
-
:
|
236
|
-
|
237
|
-
|
298
|
+
if google_json_key_location
|
299
|
+
options.merge!({:google_json_key_location => google_json_key_location, :google_project => google_project})
|
300
|
+
elsif google_json_key_string
|
301
|
+
options.merge!({:google_json_key_string => google_json_key_string, :google_project => google_project})
|
302
|
+
else
|
303
|
+
options.merge!({
|
304
|
+
:google_storage_secret_access_key => google_storage_secret_access_key,
|
305
|
+
:google_storage_access_key_id => google_storage_access_key_id
|
306
|
+
})
|
307
|
+
end
|
238
308
|
options.merge!({:region => fog_region}) if fog_region
|
239
309
|
elsif azure_rm?
|
240
310
|
require 'fog/azurerm'
|
@@ -243,11 +313,18 @@ module AssetSync
|
|
243
313
|
:azure_storage_access_key => azure_storage_access_key,
|
244
314
|
})
|
245
315
|
options.merge!({:environment => fog_region}) if fog_region
|
316
|
+
elsif backblaze?
|
317
|
+
require 'fog/backblaze'
|
318
|
+
options.merge!({
|
319
|
+
:b2_key_id => b2_key_id,
|
320
|
+
:b2_key_token => b2_key_token,
|
321
|
+
:b2_bucket_id => b2_bucket_id,
|
322
|
+
})
|
246
323
|
else
|
247
324
|
raise ArgumentError, "AssetSync Unknown provider: #{fog_provider} only AWS, Rackspace and Google are supported currently."
|
248
325
|
end
|
249
326
|
|
250
|
-
|
327
|
+
options
|
251
328
|
end
|
252
329
|
|
253
330
|
# @api
|
@@ -268,6 +345,15 @@ module AssetSync
|
|
268
345
|
end
|
269
346
|
end
|
270
347
|
|
348
|
+
#@api
|
349
|
+
def file_ext_to_mime_type_overrides
|
350
|
+
@file_ext_to_mime_type_overrides ||= FileExtToMimeTypeOverrides.new
|
351
|
+
end
|
352
|
+
|
353
|
+
def fog_public=(new_val)
|
354
|
+
@fog_public = FogPublicValue.new(new_val)
|
355
|
+
end
|
356
|
+
|
271
357
|
private
|
272
358
|
|
273
359
|
# This is a proc to get additional local files paths
|
@@ -277,5 +363,57 @@ module AssetSync
|
|
277
363
|
def default_manifest_directory
|
278
364
|
File.join(::Rails.public_path, assets_prefix)
|
279
365
|
end
|
366
|
+
|
367
|
+
|
368
|
+
# @api private
|
369
|
+
class FileExtToMimeTypeOverrides
|
370
|
+
def initialize
|
371
|
+
# The default is to prevent new mime type `application/ecmascript` to be returned
|
372
|
+
# which disables compression on some CDNs
|
373
|
+
@overrides = {
|
374
|
+
"js" => "application/javascript",
|
375
|
+
}
|
376
|
+
end
|
377
|
+
|
378
|
+
# @api
|
379
|
+
def add(ext, mime_type)
|
380
|
+
# Symbol / Mime type object might be passed in
|
381
|
+
# But we want strings only
|
382
|
+
@overrides.store(
|
383
|
+
ext.to_s, mime_type.to_s,
|
384
|
+
)
|
385
|
+
end
|
386
|
+
|
387
|
+
# @api
|
388
|
+
def clear
|
389
|
+
@overrides = {}
|
390
|
+
end
|
391
|
+
|
392
|
+
|
393
|
+
# @api private
|
394
|
+
def key?(key)
|
395
|
+
@overrides.key?(key)
|
396
|
+
end
|
397
|
+
|
398
|
+
# @api private
|
399
|
+
def fetch(key)
|
400
|
+
@overrides.fetch(key)
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
# @api private
|
405
|
+
class FogPublicValue
|
406
|
+
def initialize(val)
|
407
|
+
@value = val
|
408
|
+
end
|
409
|
+
|
410
|
+
def use_explicit_value?
|
411
|
+
@value.to_s != "default"
|
412
|
+
end
|
413
|
+
|
414
|
+
def to_bool
|
415
|
+
!!@value
|
416
|
+
end
|
417
|
+
end
|
280
418
|
end
|
281
419
|
end
|
data/lib/asset_sync/engine.rb
CHANGED
@@ -17,11 +17,13 @@ module AssetSync
|
|
17
17
|
config.fog_directory = ENV['FOG_DIRECTORY'] if ENV.has_key?('FOG_DIRECTORY')
|
18
18
|
config.fog_region = ENV['FOG_REGION'] if ENV.has_key?('FOG_REGION')
|
19
19
|
config.fog_host = ENV['FOG_HOST'] if ENV.has_key?('FOG_HOST')
|
20
|
+
config.fog_port = ENV['FOG_PORT'] if ENV.has_key?('FOG_PORT')
|
20
21
|
config.fog_scheme = ENV['FOG_SCHEMA'] if ENV.has_key?('FOG_SCHEMA')
|
21
22
|
config.fog_path_style = ENV['FOG_PATH_STYLE'] if ENV.has_key?('FOG_PATH_STYLE')
|
22
23
|
|
23
24
|
config.aws_access_key_id = ENV['AWS_ACCESS_KEY_ID'] if ENV.has_key?('AWS_ACCESS_KEY_ID')
|
24
25
|
config.aws_secret_access_key = ENV['AWS_SECRET_ACCESS_KEY'] if ENV.has_key?('AWS_SECRET_ACCESS_KEY')
|
26
|
+
config.aws_session_token = ENV['AWS_SESSION_TOKEN'] if ENV.has_key?('AWS_SESSION_TOKEN')
|
25
27
|
config.aws_signature_version = ENV['AWS_SIGNATURE_VERSION'] if ENV.has_key?('AWS_SIGNATURE_VERSION')
|
26
28
|
config.aws_reduced_redundancy = ENV['AWS_REDUCED_REDUNDANCY'] == true if ENV.has_key?('AWS_REDUCED_REDUNDANCY')
|
27
29
|
|
@@ -34,6 +36,10 @@ module AssetSync
|
|
34
36
|
config.azure_storage_account_name = ENV['AZURE_STORAGE_ACCOUNT_NAME'] if ENV.has_key?('AZURE_STORAGE_ACCOUNT_NAME')
|
35
37
|
config.azure_storage_access_key = ENV['AZURE_STORAGE_ACCESS_KEY'] if ENV.has_key?('AZURE_STORAGE_ACCESS_KEY')
|
36
38
|
|
39
|
+
config.b2_key_id = ENV['B2_KEY_ID'] if ENV.has_key?('B2_KEY_ID')
|
40
|
+
config.b2_key_token = ENV['B2_KEY_TOKEN'] if ENV.has_key?('B2_KEY_TOKEN')
|
41
|
+
config.b2_bucket_id = ENV['B2_BUCKET_ID'] if ENV.has_key?('B2_BUCKET_ID')
|
42
|
+
|
37
43
|
config.enabled = (ENV['ASSET_SYNC_ENABLED'] == 'true') if ENV.has_key?('ASSET_SYNC_ENABLED')
|
38
44
|
|
39
45
|
config.existing_remote_files = ENV['ASSET_SYNC_EXISTING_REMOTE_FILES'] || "keep"
|
@@ -41,6 +47,8 @@ module AssetSync
|
|
41
47
|
config.gzip_compression = (ENV['ASSET_SYNC_GZIP_COMPRESSION'] == 'true') if ENV.has_key?('ASSET_SYNC_GZIP_COMPRESSION')
|
42
48
|
config.manifest = (ENV['ASSET_SYNC_MANIFEST'] == 'true') if ENV.has_key?('ASSET_SYNC_MANIFEST')
|
43
49
|
config.include_manifest = (ENV['ASSET_SYNC_INCLUDE_MANIFEST'] == 'true') if ENV.has_key?('ASSET_SYNC_INCLUDE_MANIFEST')
|
50
|
+
config.concurrent_uploads = (ENV['ASSET_SYNC_CONCURRENT_UPLOADS'] == 'true') if ENV.has_key?('ASSET_SYNC_CONCURRENT_UPLOADS')
|
51
|
+
config.remote_file_list_cache_file_path = ENV['ASSET_SYNC_REMOTE_FILE_LIST_CACHE_FILE_PATH'] if ENV.has_key?('ASSET_SYNC_REMOTE_FILE_LIST_CACHE_FILE_PATH')
|
44
52
|
end
|
45
53
|
|
46
54
|
config.prefix = ENV['ASSET_SYNC_PREFIX'] if ENV.has_key?('ASSET_SYNC_PREFIX')
|
@@ -4,11 +4,16 @@ module AssetSync
|
|
4
4
|
class MultiMime
|
5
5
|
|
6
6
|
def self.lookup(ext)
|
7
|
+
overrides =
|
8
|
+
::AssetSync.config.file_ext_to_mime_type_overrides
|
9
|
+
if overrides.key?(ext)
|
10
|
+
return overrides.fetch(ext)
|
11
|
+
end
|
7
12
|
|
8
13
|
if defined?(::MIME::Types)
|
9
|
-
::MIME::Types.type_for(ext).first
|
14
|
+
::MIME::Types.type_for(ext).first.to_s
|
10
15
|
elsif defined?(::Mime::Type)
|
11
|
-
::Mime::Type.lookup_by_extension(ext)
|
16
|
+
::Mime::Type.lookup_by_extension(ext).to_s
|
12
17
|
elsif defined?(::Rack::Mime)
|
13
18
|
ext_with_dot = ".#{ext}"
|
14
19
|
::Rack::Mime.mime_type(ext_with_dot)
|
data/lib/asset_sync/storage.rb
CHANGED
@@ -4,7 +4,7 @@ require "asset_sync/multi_mime"
|
|
4
4
|
|
5
5
|
module AssetSync
|
6
6
|
class Storage
|
7
|
-
REGEXP_FINGERPRINTED_FILES =
|
7
|
+
REGEXP_FINGERPRINTED_FILES = /\A(.*)\/(.+)-[^\.]+\.([^\.]+)\z/m
|
8
8
|
REGEXP_ASSETS_TO_CACHE_CONTROL = /-[0-9a-fA-F]{32,}$/
|
9
9
|
|
10
10
|
class BucketNotFound < StandardError;
|
@@ -22,7 +22,13 @@ module AssetSync
|
|
22
22
|
|
23
23
|
def bucket
|
24
24
|
# fixes: https://github.com/rumblelabs/asset_sync/issues/18
|
25
|
-
|
25
|
+
|
26
|
+
@bucket ||= if self.config.backblaze?
|
27
|
+
connection.directories.get(self.config.fog_directory)
|
28
|
+
else
|
29
|
+
connection.directories.get(self.config.fog_directory, :prefix => self.config.assets_prefix)
|
30
|
+
end
|
31
|
+
|
26
32
|
end
|
27
33
|
|
28
34
|
def log(msg)
|
@@ -37,13 +43,17 @@ module AssetSync
|
|
37
43
|
self.config.public_path
|
38
44
|
end
|
39
45
|
|
46
|
+
def remote_file_list_cache_file_path
|
47
|
+
self.config.remote_file_list_cache_file_path
|
48
|
+
end
|
49
|
+
|
40
50
|
def ignored_files
|
41
51
|
expand_file_names(self.config.ignored_files)
|
42
52
|
end
|
43
53
|
|
44
54
|
def get_manifest_path
|
45
55
|
return [] unless self.config.include_manifest
|
46
|
-
|
56
|
+
|
47
57
|
if ActionView::Base.respond_to?(:assets_manifest)
|
48
58
|
manifest = Sprockets::Manifest.new(ActionView::Base.assets_manifest.environment, ActionView::Base.assets_manifest.dir)
|
49
59
|
manifest_path = manifest.filename
|
@@ -58,6 +68,32 @@ module AssetSync
|
|
58
68
|
(get_local_files + config.additional_local_file_paths).uniq
|
59
69
|
end
|
60
70
|
|
71
|
+
def remote_files
|
72
|
+
return [] if ignore_existing_remote_files?
|
73
|
+
return @remote_files if @remote_files
|
74
|
+
|
75
|
+
if remote_file_list_cache_file_path && File.file?(remote_file_list_cache_file_path)
|
76
|
+
begin
|
77
|
+
content = File.read(remote_file_list_cache_file_path)
|
78
|
+
return @remote_files = JSON.parse(content)
|
79
|
+
rescue JSON::ParserError
|
80
|
+
warn "Failed to parse #{remote_file_list_cache_file_path} as json"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
@remote_files = get_remote_files
|
85
|
+
end
|
86
|
+
|
87
|
+
def update_remote_file_list_cache(local_files_to_upload)
|
88
|
+
return unless remote_file_list_cache_file_path
|
89
|
+
return if ignore_existing_remote_files?
|
90
|
+
|
91
|
+
File.open(self.remote_file_list_cache_file_path, 'w') do |file|
|
92
|
+
uploaded = local_files_to_upload + remote_files
|
93
|
+
file.write(uploaded.to_json)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
61
97
|
def always_upload_files
|
62
98
|
expand_file_names(self.config.always_upload) + get_manifest_path
|
63
99
|
end
|
@@ -132,9 +168,15 @@ module AssetSync
|
|
132
168
|
from_remote_files_to_delete = remote_files - local_files - ignored_files - always_upload_files
|
133
169
|
|
134
170
|
log "Flagging #{from_remote_files_to_delete.size} file(s) for deletion"
|
135
|
-
# Delete unneeded remote files
|
136
|
-
|
137
|
-
|
171
|
+
# Delete unneeded remote files, if we are on aws delete in bulk else use sequential delete
|
172
|
+
if self.config.aws? && connection.respond_to?(:delete_multiple_objects)
|
173
|
+
from_remote_files_to_delete.each_slice(500) do |slice|
|
174
|
+
connection.delete_multiple_objects(config.fog_directory, slice)
|
175
|
+
end
|
176
|
+
else
|
177
|
+
bucket.files.each do |f|
|
178
|
+
delete_file(f, from_remote_files_to_delete)
|
179
|
+
end
|
138
180
|
end
|
139
181
|
end
|
140
182
|
|
@@ -148,10 +190,17 @@ module AssetSync
|
|
148
190
|
file = {
|
149
191
|
:key => f,
|
150
192
|
:body => file_handle,
|
151
|
-
:public => true,
|
152
193
|
:content_type => mime
|
153
194
|
}
|
154
195
|
|
196
|
+
# region fog_public
|
197
|
+
|
198
|
+
if config.fog_public.use_explicit_value?
|
199
|
+
file[:public] = config.fog_public.to_bool
|
200
|
+
end
|
201
|
+
|
202
|
+
# endregion fog_public
|
203
|
+
|
155
204
|
uncompressed_filename = f.sub(/\.gz\z/, '')
|
156
205
|
basename = File.basename(uncompressed_filename, File.extname(uncompressed_filename))
|
157
206
|
|
@@ -224,28 +273,38 @@ module AssetSync
|
|
224
273
|
})
|
225
274
|
end
|
226
275
|
|
227
|
-
if config.azure_rm?
|
228
|
-
# converts content_type from MIME::Type to String.
|
229
|
-
# because Azure::Storage (called from Fog::AzureRM) expects content_type as a String like "application/json; charset=utf-8"
|
230
|
-
file[:content_type] = file[:content_type].content_type if file[:content_type].is_a?(::MIME::Type)
|
231
|
-
end
|
232
|
-
|
233
276
|
bucket.files.create( file ) unless ignore
|
234
277
|
file_handle.close
|
235
278
|
gzip_file_handle.close if gzip_file_handle
|
236
279
|
end
|
237
280
|
|
238
281
|
def upload_files
|
239
|
-
# get a fresh list of remote files
|
240
|
-
remote_files = ignore_existing_remote_files? ? [] : get_remote_files
|
241
282
|
# fixes: https://github.com/rumblelabs/asset_sync/issues/19
|
242
283
|
local_files_to_upload = local_files - ignored_files - remote_files + always_upload_files
|
243
284
|
local_files_to_upload = (local_files_to_upload + get_non_fingerprinted(local_files_to_upload)).uniq
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
285
|
+
# Only files.
|
286
|
+
local_files_to_upload = local_files_to_upload.select { |f| File.file? "#{path}/#{f}" }
|
287
|
+
|
288
|
+
if self.config.concurrent_uploads
|
289
|
+
jobs = Queue.new
|
290
|
+
local_files_to_upload.each { |f| jobs.push(f) }
|
291
|
+
jobs.close
|
292
|
+
|
293
|
+
num_threads = [self.config.concurrent_uploads_max_threads, local_files_to_upload.length].min
|
294
|
+
# Upload new files
|
295
|
+
workers = Array.new(num_threads) do
|
296
|
+
Thread.new do
|
297
|
+
while f = jobs.pop
|
298
|
+
upload_file(f)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
workers.map(&:join)
|
303
|
+
else
|
304
|
+
# Upload new files
|
305
|
+
local_files_to_upload.each do |f|
|
306
|
+
upload_file f
|
307
|
+
end
|
249
308
|
end
|
250
309
|
|
251
310
|
if self.config.cdn_distribution_id && files_to_invalidate.any?
|
@@ -254,6 +313,8 @@ module AssetSync
|
|
254
313
|
data = cdn.post_invalidation(self.config.cdn_distribution_id, files_to_invalidate)
|
255
314
|
log "Invalidation id: #{data.body["Id"]}"
|
256
315
|
end
|
316
|
+
|
317
|
+
update_remote_file_list_cache(local_files_to_upload)
|
257
318
|
end
|
258
319
|
|
259
320
|
def sync
|