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.
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 "jeweler"
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", "~> 4.1.0"
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", "~> 4.2.0"
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", "~> 5.0.0"
8
+ gem "rails", "~> 6.1.0"
9
9
 
10
10
  gemspec path: "../"
@@ -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 => :google?
60
- validates :google_storage_access_key_id, :presence => true, :if => :google?
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.google_storage_secret_access_key = yml["google_storage_secret_access_key"]
175
- self.google_storage_access_key_id = yml["google_storage_access_key_id"]
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
- options.merge!({
235
- :google_storage_secret_access_key => google_storage_secret_access_key,
236
- :google_storage_access_key_id => google_storage_access_key_id
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
- return options
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
@@ -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)
@@ -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
- @bucket ||= connection.directories.get(self.config.fog_directory, :prefix => self.config.assets_prefix)
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
- bucket.files.each do |f|
137
- delete_file(f, from_remote_files_to_delete)
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
- # Upload new files
246
- local_files_to_upload.each do |f|
247
- next unless File.file? "#{path}/#{f}" # Only files.
248
- upload_file f
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