asset_sync 2.4.0 → 2.15.2
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 +68 -0
- data/.travis.yml +25 -26
- data/Appraisals +6 -10
- data/CHANGELOG.md +207 -2
- data/README.md +135 -10
- data/asset_sync.gemspec +7 -4
- 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/asset_sync.rb +10 -0
- data/lib/asset_sync/config.rb +163 -15
- data/lib/asset_sync/engine.rb +9 -0
- data/lib/asset_sync/multi_mime.rb +7 -2
- data/lib/asset_sync/storage.rb +84 -21
- 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 +26 -0
- data/lib/generators/asset_sync/templates/asset_sync.yml +25 -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 +60 -11
- 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,14 +22,19 @@ 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
|
-
s.add_development_dependency "fog-azure-rm"
|
30
|
+
s.add_development_dependency "gitlab-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"
|
34
35
|
|
36
|
+
s.add_development_dependency "gem-release"
|
37
|
+
|
35
38
|
s.files = `git ls-files`.split("\n")
|
36
39
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
37
40
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
@@ -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: "../"
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
1
3
|
module AssetSync
|
2
4
|
|
3
5
|
class << self
|
@@ -60,6 +62,14 @@ module AssetSync
|
|
60
62
|
stdout.puts msg unless config.log_silently?
|
61
63
|
end
|
62
64
|
|
65
|
+
def load_yaml(yaml)
|
66
|
+
if YAML.respond_to?(:unsafe_load)
|
67
|
+
YAML.unsafe_load(yaml)
|
68
|
+
else
|
69
|
+
YAML.load(yaml)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
63
73
|
def enabled?
|
64
74
|
config.enabled?
|
65
75
|
end
|
data/lib/asset_sync/config.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_model"
|
2
4
|
require "erb"
|
3
|
-
require "yaml"
|
4
5
|
|
5
6
|
module AssetSync
|
6
7
|
class Config
|
@@ -17,7 +18,6 @@ module AssetSync
|
|
17
18
|
attr_accessor :always_upload
|
18
19
|
attr_accessor :ignored_files
|
19
20
|
attr_accessor :prefix
|
20
|
-
attr_accessor :public_path
|
21
21
|
attr_accessor :enabled
|
22
22
|
attr_accessor :custom_headers
|
23
23
|
attr_accessor :run_on_precompile
|
@@ -25,15 +25,28 @@ module AssetSync
|
|
25
25
|
attr_accessor :cdn_distribution_id
|
26
26
|
attr_accessor :cache_asset_regexps
|
27
27
|
attr_accessor :include_manifest
|
28
|
+
attr_accessor :concurrent_uploads
|
29
|
+
attr_accessor :concurrent_uploads_max_threads
|
30
|
+
attr_accessor :remote_file_list_cache_file_path
|
28
31
|
|
29
32
|
# FOG configuration
|
30
33
|
attr_accessor :fog_provider # Currently Supported ['AWS', 'Rackspace']
|
31
34
|
attr_accessor :fog_directory # e.g. 'the-bucket-name'
|
32
35
|
attr_accessor :fog_region # e.g. 'eu-west-1'
|
36
|
+
attr_reader :fog_public # e.g. true, false, "default"
|
33
37
|
|
34
38
|
# Amazon AWS
|
35
|
-
attr_accessor :aws_access_key_id
|
39
|
+
attr_accessor :aws_access_key_id
|
40
|
+
attr_accessor :aws_secret_access_key
|
41
|
+
attr_accessor :aws_session_token
|
42
|
+
attr_accessor :aws_reduced_redundancy
|
43
|
+
attr_accessor :aws_iam_roles
|
44
|
+
attr_accessor :aws_signature_version
|
45
|
+
attr_accessor :aws_acl
|
46
|
+
|
47
|
+
# Fog
|
36
48
|
attr_accessor :fog_host # e.g. 's3.amazonaws.com'
|
49
|
+
attr_accessor :fog_port # e.g. '9000'
|
37
50
|
attr_accessor :fog_path_style # e.g. true
|
38
51
|
attr_accessor :fog_scheme # e.g. 'http'
|
39
52
|
|
@@ -41,12 +54,20 @@ module AssetSync
|
|
41
54
|
attr_accessor :rackspace_username, :rackspace_api_key, :rackspace_auth_url
|
42
55
|
|
43
56
|
# Google Storage
|
44
|
-
attr_accessor :google_storage_secret_access_key, :google_storage_access_key_id
|
57
|
+
attr_accessor :google_storage_secret_access_key, :google_storage_access_key_id # when using S3 interop
|
58
|
+
attr_accessor :google_json_key_location # when using service accounts
|
59
|
+
attr_accessor :google_json_key_string # when using service accounts
|
60
|
+
attr_accessor :google_project # when using service accounts
|
45
61
|
|
46
62
|
# Azure Blob with Fog::AzureRM
|
47
63
|
attr_accessor :azure_storage_account_name
|
48
64
|
attr_accessor :azure_storage_access_key
|
49
65
|
|
66
|
+
# Backblaze B2 with Fog::Backblaze
|
67
|
+
attr_accessor :b2_key_id
|
68
|
+
attr_accessor :b2_key_token
|
69
|
+
attr_accessor :b2_bucket_id
|
70
|
+
|
50
71
|
validates :existing_remote_files, :inclusion => { :in => %w(keep delete ignore) }
|
51
72
|
|
52
73
|
validates :fog_provider, :presence => true
|
@@ -56,11 +77,19 @@ module AssetSync
|
|
56
77
|
validates :aws_secret_access_key, :presence => true, :if => proc {aws? && !aws_iam?}
|
57
78
|
validates :rackspace_username, :presence => true, :if => :rackspace?
|
58
79
|
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 => :
|
80
|
+
validates :google_storage_secret_access_key, :presence => true, :if => :google_interop?
|
81
|
+
validates :google_storage_access_key_id, :presence => true, :if => :google_interop?
|
82
|
+
validates :google_project, :presence => true, :if => :google_service_account?
|
83
|
+
validate(:if => :google_service_account?) do
|
84
|
+
unless google_json_key_location.present? || google_json_key_string.present?
|
85
|
+
errors.add(:base, 'must provide either google_json_key_location or google_json_key_string if using Google service account')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
validates :concurrent_uploads, :inclusion => { :in => [true, false] }
|
61
89
|
|
62
90
|
def initialize
|
63
91
|
self.fog_region = nil
|
92
|
+
self.fog_public = true
|
64
93
|
self.existing_remote_files = 'keep'
|
65
94
|
self.gzip_compression = false
|
66
95
|
self.manifest = false
|
@@ -75,6 +104,9 @@ module AssetSync
|
|
75
104
|
self.invalidate = []
|
76
105
|
self.cache_asset_regexps = []
|
77
106
|
self.include_manifest = false
|
107
|
+
self.concurrent_uploads = false
|
108
|
+
self.concurrent_uploads_max_threads = 10
|
109
|
+
self.remote_file_list_cache_file_path = nil
|
78
110
|
@additional_local_file_paths_procs = []
|
79
111
|
|
80
112
|
load_yml! if defined?(::Rails) && yml_exists?
|
@@ -126,10 +158,22 @@ module AssetSync
|
|
126
158
|
fog_provider =~ /google/i
|
127
159
|
end
|
128
160
|
|
161
|
+
def google_interop?
|
162
|
+
google? && google_json_key_location.nil? && google_json_key_string.nil?
|
163
|
+
end
|
164
|
+
|
165
|
+
def google_service_account?
|
166
|
+
google? && (google_json_key_location || google_json_key_string)
|
167
|
+
end
|
168
|
+
|
129
169
|
def azure_rm?
|
130
170
|
fog_provider =~ /azurerm/i
|
131
171
|
end
|
132
172
|
|
173
|
+
def backblaze?
|
174
|
+
fog_provider =~ /backblaze/i
|
175
|
+
end
|
176
|
+
|
133
177
|
def cache_asset_regexp=(cache_asset_regexp)
|
134
178
|
self.cache_asset_regexps = [cache_asset_regexp]
|
135
179
|
end
|
@@ -139,7 +183,7 @@ module AssetSync
|
|
139
183
|
end
|
140
184
|
|
141
185
|
def yml
|
142
|
-
@yml ||= ::
|
186
|
+
@yml ||= ::AssetSync.load_yaml(::ERB.new(IO.read(yml_path)).result)[::Rails.env] || {}
|
143
187
|
end
|
144
188
|
|
145
189
|
def yml_path
|
@@ -155,40 +199,68 @@ module AssetSync
|
|
155
199
|
@public_path || ::Rails.public_path
|
156
200
|
end
|
157
201
|
|
202
|
+
def public_path=(path)
|
203
|
+
# Generate absolute path even when relative path passed in
|
204
|
+
# Required for generating relative sprockets manifest path
|
205
|
+
pathname = Pathname(path)
|
206
|
+
@public_path = if pathname.absolute?
|
207
|
+
pathname
|
208
|
+
elsif defined?(::Rails.root)
|
209
|
+
::Rails.root.join(pathname)
|
210
|
+
else
|
211
|
+
Pathname(::Dir.pwd).join(pathname)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
158
215
|
def load_yml!
|
159
216
|
self.enabled = yml["enabled"] if yml.has_key?('enabled')
|
160
217
|
self.fog_provider = yml["fog_provider"]
|
161
218
|
self.fog_host = yml["fog_host"]
|
219
|
+
self.fog_port = yml["fog_port"]
|
162
220
|
self.fog_directory = yml["fog_directory"]
|
163
221
|
self.fog_region = yml["fog_region"]
|
222
|
+
self.fog_public = yml["fog_public"] if yml.has_key?("fog_public")
|
164
223
|
self.fog_path_style = yml["fog_path_style"]
|
165
224
|
self.fog_scheme = yml["fog_scheme"]
|
166
225
|
self.aws_access_key_id = yml["aws_access_key_id"]
|
167
226
|
self.aws_secret_access_key = yml["aws_secret_access_key"]
|
227
|
+
self.aws_session_token = yml["aws_session_token"] if yml.has_key?("aws_session_token")
|
168
228
|
self.aws_reduced_redundancy = yml["aws_reduced_redundancy"]
|
169
229
|
self.aws_iam_roles = yml["aws_iam_roles"]
|
170
230
|
self.aws_signature_version = yml["aws_signature_version"]
|
231
|
+
self.aws_acl = yml["aws_acl"]
|
171
232
|
self.rackspace_username = yml["rackspace_username"]
|
172
233
|
self.rackspace_auth_url = yml["rackspace_auth_url"] if yml.has_key?("rackspace_auth_url")
|
173
234
|
self.rackspace_api_key = yml["rackspace_api_key"]
|
174
|
-
self.
|
175
|
-
self.
|
235
|
+
self.google_json_key_location = yml["google_json_key_location"] if yml.has_key?("google_json_key_location")
|
236
|
+
self.google_project = yml["google_project"] if yml.has_key?("google_project")
|
237
|
+
self.google_storage_secret_access_key = yml["google_storage_secret_access_key"] if yml.has_key?("google_storage_secret_access_key")
|
238
|
+
self.google_storage_access_key_id = yml["google_storage_access_key_id"] if yml.has_key?("google_storage_access_key_id")
|
239
|
+
self.google_json_key_string = yml["google_json_key_string"] if yml.has_key?("google_json_key_string")
|
176
240
|
self.existing_remote_files = yml["existing_remote_files"] if yml.has_key?("existing_remote_files")
|
177
241
|
self.gzip_compression = yml["gzip_compression"] if yml.has_key?("gzip_compression")
|
178
242
|
self.manifest = yml["manifest"] if yml.has_key?("manifest")
|
179
243
|
self.fail_silently = yml["fail_silently"] if yml.has_key?("fail_silently")
|
244
|
+
self.log_silently = yml["log_silently"] if yml.has_key?("log_silently")
|
180
245
|
self.always_upload = yml["always_upload"] if yml.has_key?("always_upload")
|
181
246
|
self.ignored_files = yml["ignored_files"] if yml.has_key?("ignored_files")
|
182
|
-
self.custom_headers
|
247
|
+
self.custom_headers = yml["custom_headers"] if yml.has_key?("custom_headers")
|
183
248
|
self.run_on_precompile = yml["run_on_precompile"] if yml.has_key?("run_on_precompile")
|
184
249
|
self.invalidate = yml["invalidate"] if yml.has_key?("invalidate")
|
185
250
|
self.cdn_distribution_id = yml['cdn_distribution_id'] if yml.has_key?("cdn_distribution_id")
|
186
251
|
self.cache_asset_regexps = yml['cache_asset_regexps'] if yml.has_key?("cache_asset_regexps")
|
187
252
|
self.include_manifest = yml['include_manifest'] if yml.has_key?("include_manifest")
|
253
|
+
self.concurrent_uploads = yml['concurrent_uploads'] if yml.has_key?('concurrent_uploads')
|
254
|
+
self.concurrent_uploads_max_threads = yml['concurrent_uploads_max_threads'] if yml.has_key?('concurrent_uploads_max_threads')
|
255
|
+
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
256
|
|
189
257
|
self.azure_storage_account_name = yml['azure_storage_account_name'] if yml.has_key?("azure_storage_account_name")
|
190
258
|
self.azure_storage_access_key = yml['azure_storage_access_key'] if yml.has_key?("azure_storage_access_key")
|
191
259
|
|
260
|
+
self.b2_key_id = yml['b2_key_id'] if yml.has_key?("b2_key_id")
|
261
|
+
self.b2_key_token = yml['b2_key_token'] if yml.has_key?("b2_key_token")
|
262
|
+
self.b2_bucket_id = yml['b2_bucket_id'] if yml.has_key?("b2_bucket_id")
|
263
|
+
|
192
264
|
# TODO deprecate the other old style config settings. FML.
|
193
265
|
self.aws_access_key_id = yml["aws_access_key"] if yml.has_key?("aws_access_key")
|
194
266
|
self.aws_secret_access_key = yml["aws_access_secret"] if yml.has_key?("aws_access_secret")
|
@@ -217,8 +289,10 @@ module AssetSync
|
|
217
289
|
:aws_access_key_id => aws_access_key_id,
|
218
290
|
:aws_secret_access_key => aws_secret_access_key
|
219
291
|
})
|
292
|
+
options.merge!({:aws_session_token => aws_session_token}) if aws_session_token
|
220
293
|
end
|
221
294
|
options.merge!({:host => fog_host}) if fog_host
|
295
|
+
options.merge!({:port => fog_port}) if fog_port
|
222
296
|
options.merge!({:scheme => fog_scheme}) if fog_scheme
|
223
297
|
options.merge!({:aws_signature_version => aws_signature_version}) if aws_signature_version
|
224
298
|
options.merge!({:path_style => fog_path_style}) if fog_path_style
|
@@ -231,10 +305,16 @@ module AssetSync
|
|
231
305
|
options.merge!({ :rackspace_region => fog_region }) if fog_region
|
232
306
|
options.merge!({ :rackspace_auth_url => rackspace_auth_url }) if rackspace_auth_url
|
233
307
|
elsif google?
|
234
|
-
|
235
|
-
:
|
236
|
-
|
237
|
-
|
308
|
+
if google_json_key_location
|
309
|
+
options.merge!({:google_json_key_location => google_json_key_location, :google_project => google_project})
|
310
|
+
elsif google_json_key_string
|
311
|
+
options.merge!({:google_json_key_string => google_json_key_string, :google_project => google_project})
|
312
|
+
else
|
313
|
+
options.merge!({
|
314
|
+
:google_storage_secret_access_key => google_storage_secret_access_key,
|
315
|
+
:google_storage_access_key_id => google_storage_access_key_id
|
316
|
+
})
|
317
|
+
end
|
238
318
|
options.merge!({:region => fog_region}) if fog_region
|
239
319
|
elsif azure_rm?
|
240
320
|
require 'fog/azurerm'
|
@@ -243,11 +323,18 @@ module AssetSync
|
|
243
323
|
:azure_storage_access_key => azure_storage_access_key,
|
244
324
|
})
|
245
325
|
options.merge!({:environment => fog_region}) if fog_region
|
326
|
+
elsif backblaze?
|
327
|
+
require 'fog/backblaze'
|
328
|
+
options.merge!({
|
329
|
+
:b2_key_id => b2_key_id,
|
330
|
+
:b2_key_token => b2_key_token,
|
331
|
+
:b2_bucket_id => b2_bucket_id,
|
332
|
+
})
|
246
333
|
else
|
247
334
|
raise ArgumentError, "AssetSync Unknown provider: #{fog_provider} only AWS, Rackspace and Google are supported currently."
|
248
335
|
end
|
249
336
|
|
250
|
-
|
337
|
+
options
|
251
338
|
end
|
252
339
|
|
253
340
|
# @api
|
@@ -268,6 +355,15 @@ module AssetSync
|
|
268
355
|
end
|
269
356
|
end
|
270
357
|
|
358
|
+
#@api
|
359
|
+
def file_ext_to_mime_type_overrides
|
360
|
+
@file_ext_to_mime_type_overrides ||= FileExtToMimeTypeOverrides.new
|
361
|
+
end
|
362
|
+
|
363
|
+
def fog_public=(new_val)
|
364
|
+
@fog_public = FogPublicValue.new(new_val)
|
365
|
+
end
|
366
|
+
|
271
367
|
private
|
272
368
|
|
273
369
|
# This is a proc to get additional local files paths
|
@@ -277,5 +373,57 @@ module AssetSync
|
|
277
373
|
def default_manifest_directory
|
278
374
|
File.join(::Rails.public_path, assets_prefix)
|
279
375
|
end
|
376
|
+
|
377
|
+
|
378
|
+
# @api private
|
379
|
+
class FileExtToMimeTypeOverrides
|
380
|
+
def initialize
|
381
|
+
# The default is to prevent new mime type `application/ecmascript` to be returned
|
382
|
+
# which disables compression on some CDNs
|
383
|
+
@overrides = {
|
384
|
+
"js" => "application/javascript",
|
385
|
+
}
|
386
|
+
end
|
387
|
+
|
388
|
+
# @api
|
389
|
+
def add(ext, mime_type)
|
390
|
+
# Symbol / Mime type object might be passed in
|
391
|
+
# But we want strings only
|
392
|
+
@overrides.store(
|
393
|
+
ext.to_s, mime_type.to_s,
|
394
|
+
)
|
395
|
+
end
|
396
|
+
|
397
|
+
# @api
|
398
|
+
def clear
|
399
|
+
@overrides = {}
|
400
|
+
end
|
401
|
+
|
402
|
+
|
403
|
+
# @api private
|
404
|
+
def key?(key)
|
405
|
+
@overrides.key?(key)
|
406
|
+
end
|
407
|
+
|
408
|
+
# @api private
|
409
|
+
def fetch(key)
|
410
|
+
@overrides.fetch(key)
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
# @api private
|
415
|
+
class FogPublicValue
|
416
|
+
def initialize(val)
|
417
|
+
@value = val
|
418
|
+
end
|
419
|
+
|
420
|
+
def use_explicit_value?
|
421
|
+
@value.to_s != "default"
|
422
|
+
end
|
423
|
+
|
424
|
+
def to_bool
|
425
|
+
!!@value
|
426
|
+
end
|
427
|
+
end
|
280
428
|
end
|
281
429
|
end
|
data/lib/asset_sync/engine.rb
CHANGED
@@ -17,12 +17,15 @@ 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')
|
28
|
+
config.aws_acl = ENV['AWS_ACL'] if ENV.has_key?('AWS_ACL')
|
26
29
|
config.aws_reduced_redundancy = ENV['AWS_REDUCED_REDUNDANCY'] == true if ENV.has_key?('AWS_REDUCED_REDUNDANCY')
|
27
30
|
|
28
31
|
config.rackspace_username = ENV['RACKSPACE_USERNAME'] if ENV.has_key?('RACKSPACE_USERNAME')
|
@@ -34,6 +37,10 @@ module AssetSync
|
|
34
37
|
config.azure_storage_account_name = ENV['AZURE_STORAGE_ACCOUNT_NAME'] if ENV.has_key?('AZURE_STORAGE_ACCOUNT_NAME')
|
35
38
|
config.azure_storage_access_key = ENV['AZURE_STORAGE_ACCESS_KEY'] if ENV.has_key?('AZURE_STORAGE_ACCESS_KEY')
|
36
39
|
|
40
|
+
config.b2_key_id = ENV['B2_KEY_ID'] if ENV.has_key?('B2_KEY_ID')
|
41
|
+
config.b2_key_token = ENV['B2_KEY_TOKEN'] if ENV.has_key?('B2_KEY_TOKEN')
|
42
|
+
config.b2_bucket_id = ENV['B2_BUCKET_ID'] if ENV.has_key?('B2_BUCKET_ID')
|
43
|
+
|
37
44
|
config.enabled = (ENV['ASSET_SYNC_ENABLED'] == 'true') if ENV.has_key?('ASSET_SYNC_ENABLED')
|
38
45
|
|
39
46
|
config.existing_remote_files = ENV['ASSET_SYNC_EXISTING_REMOTE_FILES'] || "keep"
|
@@ -41,6 +48,8 @@ module AssetSync
|
|
41
48
|
config.gzip_compression = (ENV['ASSET_SYNC_GZIP_COMPRESSION'] == 'true') if ENV.has_key?('ASSET_SYNC_GZIP_COMPRESSION')
|
42
49
|
config.manifest = (ENV['ASSET_SYNC_MANIFEST'] == 'true') if ENV.has_key?('ASSET_SYNC_MANIFEST')
|
43
50
|
config.include_manifest = (ENV['ASSET_SYNC_INCLUDE_MANIFEST'] == 'true') if ENV.has_key?('ASSET_SYNC_INCLUDE_MANIFEST')
|
51
|
+
config.concurrent_uploads = (ENV['ASSET_SYNC_CONCURRENT_UPLOADS'] == 'true') if ENV.has_key?('ASSET_SYNC_CONCURRENT_UPLOADS')
|
52
|
+
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
53
|
end
|
45
54
|
|
46
55
|
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
|
@@ -81,7 +117,7 @@ module AssetSync
|
|
81
117
|
return manifest.assets.values.map { |f| File.join(self.config.assets_prefix, f) }
|
82
118
|
elsif File.exist?(self.config.manifest_path)
|
83
119
|
log "Using: Manifest #{self.config.manifest_path}"
|
84
|
-
yml =
|
120
|
+
yml = AssetSync.load_yaml(IO.read(self.config.manifest_path))
|
85
121
|
|
86
122
|
return yml.map do |original, compiled|
|
87
123
|
# Upload font originals and compiled
|
@@ -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,19 @@ 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.aws? && config.aws_acl
|
199
|
+
file[:acl] = config.aws_acl
|
200
|
+
elsif config.fog_public.use_explicit_value?
|
201
|
+
file[:public] = config.fog_public.to_bool
|
202
|
+
end
|
203
|
+
|
204
|
+
# endregion fog_public
|
205
|
+
|
155
206
|
uncompressed_filename = f.sub(/\.gz\z/, '')
|
156
207
|
basename = File.basename(uncompressed_filename, File.extname(uncompressed_filename))
|
157
208
|
|
@@ -224,28 +275,38 @@ module AssetSync
|
|
224
275
|
})
|
225
276
|
end
|
226
277
|
|
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
278
|
bucket.files.create( file ) unless ignore
|
234
279
|
file_handle.close
|
235
280
|
gzip_file_handle.close if gzip_file_handle
|
236
281
|
end
|
237
282
|
|
238
283
|
def upload_files
|
239
|
-
# get a fresh list of remote files
|
240
|
-
remote_files = ignore_existing_remote_files? ? [] : get_remote_files
|
241
284
|
# fixes: https://github.com/rumblelabs/asset_sync/issues/19
|
242
285
|
local_files_to_upload = local_files - ignored_files - remote_files + always_upload_files
|
243
286
|
local_files_to_upload = (local_files_to_upload + get_non_fingerprinted(local_files_to_upload)).uniq
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
287
|
+
# Only files.
|
288
|
+
local_files_to_upload = local_files_to_upload.select { |f| File.file? "#{path}/#{f}" }
|
289
|
+
|
290
|
+
if self.config.concurrent_uploads
|
291
|
+
jobs = Queue.new
|
292
|
+
local_files_to_upload.each { |f| jobs.push(f) }
|
293
|
+
jobs.close
|
294
|
+
|
295
|
+
num_threads = [self.config.concurrent_uploads_max_threads, local_files_to_upload.length].min
|
296
|
+
# Upload new files
|
297
|
+
workers = Array.new(num_threads) do
|
298
|
+
Thread.new do
|
299
|
+
while f = jobs.pop
|
300
|
+
upload_file(f)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
workers.map(&:join)
|
305
|
+
else
|
306
|
+
# Upload new files
|
307
|
+
local_files_to_upload.each do |f|
|
308
|
+
upload_file f
|
309
|
+
end
|
249
310
|
end
|
250
311
|
|
251
312
|
if self.config.cdn_distribution_id && files_to_invalidate.any?
|
@@ -254,6 +315,8 @@ module AssetSync
|
|
254
315
|
data = cdn.post_invalidation(self.config.cdn_distribution_id, files_to_invalidate)
|
255
316
|
log "Invalidation id: #{data.body["Id"]}"
|
256
317
|
end
|
318
|
+
|
319
|
+
update_remote_file_list_cache(local_files_to_upload)
|
257
320
|
end
|
258
321
|
|
259
322
|
def sync
|
data/lib/asset_sync/version.rb
CHANGED