asset_sync 2.8.1 → 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 +6 -21
- data/Appraisals +5 -13
- data/CHANGELOG.md +140 -1
- data/README.md +58 -6
- data/asset_sync.gemspec +4 -1
- data/gemfiles/rails_6_0.gemfile +1 -1
- data/gemfiles/{rails_4_2.gemfile → rails_6_1.gemfile} +1 -1
- data/lib/asset_sync/asset_sync.rb +10 -0
- data/lib/asset_sync/config.rb +69 -8
- data/lib/asset_sync/engine.rb +8 -0
- data/lib/asset_sync/storage.rb +76 -14
- 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 +18 -0
- data/lib/generators/asset_sync/templates/asset_sync.yml +13 -3
- data/spec/fixtures/backblaze_with_yml/config/asset_sync.yml +20 -0
- data/spec/integration/backblaze_intergration_spec.rb +74 -0
- data/spec/unit/backblaze_spec.rb +150 -0
- data/spec/unit/google_spec.rb +22 -0
- data/spec/unit/railsless_spec.rb +4 -3
- data/spec/unit/storage_spec.rb +150 -7
- metadata +40 -7
- data/gemfiles/rails_5_0.gemfile +0 -10
- data/gemfiles/rails_5_1.gemfile +0 -10
data/lib/asset_sync/config.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require "active_model"
|
4
4
|
require "erb"
|
5
|
-
require "yaml"
|
6
5
|
|
7
6
|
module AssetSync
|
8
7
|
class Config
|
@@ -26,7 +25,9 @@ module AssetSync
|
|
26
25
|
attr_accessor :cdn_distribution_id
|
27
26
|
attr_accessor :cache_asset_regexps
|
28
27
|
attr_accessor :include_manifest
|
29
|
-
|
28
|
+
attr_accessor :concurrent_uploads
|
29
|
+
attr_accessor :concurrent_uploads_max_threads
|
30
|
+
attr_accessor :remote_file_list_cache_file_path
|
30
31
|
|
31
32
|
# FOG configuration
|
32
33
|
attr_accessor :fog_provider # Currently Supported ['AWS', 'Rackspace']
|
@@ -35,7 +36,15 @@ module AssetSync
|
|
35
36
|
attr_reader :fog_public # e.g. true, false, "default"
|
36
37
|
|
37
38
|
# Amazon AWS
|
38
|
-
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
|
39
48
|
attr_accessor :fog_host # e.g. 's3.amazonaws.com'
|
40
49
|
attr_accessor :fog_port # e.g. '9000'
|
41
50
|
attr_accessor :fog_path_style # e.g. true
|
@@ -47,12 +56,18 @@ module AssetSync
|
|
47
56
|
# Google Storage
|
48
57
|
attr_accessor :google_storage_secret_access_key, :google_storage_access_key_id # when using S3 interop
|
49
58
|
attr_accessor :google_json_key_location # when using service accounts
|
59
|
+
attr_accessor :google_json_key_string # when using service accounts
|
50
60
|
attr_accessor :google_project # when using service accounts
|
51
61
|
|
52
62
|
# Azure Blob with Fog::AzureRM
|
53
63
|
attr_accessor :azure_storage_account_name
|
54
64
|
attr_accessor :azure_storage_access_key
|
55
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
|
+
|
56
71
|
validates :existing_remote_files, :inclusion => { :in => %w(keep delete ignore) }
|
57
72
|
|
58
73
|
validates :fog_provider, :presence => true
|
@@ -64,8 +79,13 @@ module AssetSync
|
|
64
79
|
validates :rackspace_api_key, :presence => true, :if => :rackspace?
|
65
80
|
validates :google_storage_secret_access_key, :presence => true, :if => :google_interop?
|
66
81
|
validates :google_storage_access_key_id, :presence => true, :if => :google_interop?
|
67
|
-
validates :google_json_key_location, :presence => true, :if => :google_service_account?
|
68
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] }
|
69
89
|
|
70
90
|
def initialize
|
71
91
|
self.fog_region = nil
|
@@ -84,6 +104,9 @@ module AssetSync
|
|
84
104
|
self.invalidate = []
|
85
105
|
self.cache_asset_regexps = []
|
86
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
|
87
110
|
@additional_local_file_paths_procs = []
|
88
111
|
|
89
112
|
load_yml! if defined?(::Rails) && yml_exists?
|
@@ -136,17 +159,21 @@ module AssetSync
|
|
136
159
|
end
|
137
160
|
|
138
161
|
def google_interop?
|
139
|
-
google? && google_json_key_location.nil?
|
162
|
+
google? && google_json_key_location.nil? && google_json_key_string.nil?
|
140
163
|
end
|
141
164
|
|
142
165
|
def google_service_account?
|
143
|
-
google? && google_json_key_location
|
166
|
+
google? && (google_json_key_location || google_json_key_string)
|
144
167
|
end
|
145
168
|
|
146
169
|
def azure_rm?
|
147
170
|
fog_provider =~ /azurerm/i
|
148
171
|
end
|
149
172
|
|
173
|
+
def backblaze?
|
174
|
+
fog_provider =~ /backblaze/i
|
175
|
+
end
|
176
|
+
|
150
177
|
def cache_asset_regexp=(cache_asset_regexp)
|
151
178
|
self.cache_asset_regexps = [cache_asset_regexp]
|
152
179
|
end
|
@@ -156,7 +183,7 @@ module AssetSync
|
|
156
183
|
end
|
157
184
|
|
158
185
|
def yml
|
159
|
-
@yml ||= ::
|
186
|
+
@yml ||= ::AssetSync.load_yaml(::ERB.new(IO.read(yml_path)).result)[::Rails.env] || {}
|
160
187
|
end
|
161
188
|
|
162
189
|
def yml_path
|
@@ -172,6 +199,19 @@ module AssetSync
|
|
172
199
|
@public_path || ::Rails.public_path
|
173
200
|
end
|
174
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
|
+
|
175
215
|
def load_yml!
|
176
216
|
self.enabled = yml["enabled"] if yml.has_key?('enabled')
|
177
217
|
self.fog_provider = yml["fog_provider"]
|
@@ -184,9 +224,11 @@ module AssetSync
|
|
184
224
|
self.fog_scheme = yml["fog_scheme"]
|
185
225
|
self.aws_access_key_id = yml["aws_access_key_id"]
|
186
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")
|
187
228
|
self.aws_reduced_redundancy = yml["aws_reduced_redundancy"]
|
188
229
|
self.aws_iam_roles = yml["aws_iam_roles"]
|
189
230
|
self.aws_signature_version = yml["aws_signature_version"]
|
231
|
+
self.aws_acl = yml["aws_acl"]
|
190
232
|
self.rackspace_username = yml["rackspace_username"]
|
191
233
|
self.rackspace_auth_url = yml["rackspace_auth_url"] if yml.has_key?("rackspace_auth_url")
|
192
234
|
self.rackspace_api_key = yml["rackspace_api_key"]
|
@@ -194,22 +236,31 @@ module AssetSync
|
|
194
236
|
self.google_project = yml["google_project"] if yml.has_key?("google_project")
|
195
237
|
self.google_storage_secret_access_key = yml["google_storage_secret_access_key"] if yml.has_key?("google_storage_secret_access_key")
|
196
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")
|
197
240
|
self.existing_remote_files = yml["existing_remote_files"] if yml.has_key?("existing_remote_files")
|
198
241
|
self.gzip_compression = yml["gzip_compression"] if yml.has_key?("gzip_compression")
|
199
242
|
self.manifest = yml["manifest"] if yml.has_key?("manifest")
|
200
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")
|
201
245
|
self.always_upload = yml["always_upload"] if yml.has_key?("always_upload")
|
202
246
|
self.ignored_files = yml["ignored_files"] if yml.has_key?("ignored_files")
|
203
|
-
self.custom_headers
|
247
|
+
self.custom_headers = yml["custom_headers"] if yml.has_key?("custom_headers")
|
204
248
|
self.run_on_precompile = yml["run_on_precompile"] if yml.has_key?("run_on_precompile")
|
205
249
|
self.invalidate = yml["invalidate"] if yml.has_key?("invalidate")
|
206
250
|
self.cdn_distribution_id = yml['cdn_distribution_id'] if yml.has_key?("cdn_distribution_id")
|
207
251
|
self.cache_asset_regexps = yml['cache_asset_regexps'] if yml.has_key?("cache_asset_regexps")
|
208
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')
|
209
256
|
|
210
257
|
self.azure_storage_account_name = yml['azure_storage_account_name'] if yml.has_key?("azure_storage_account_name")
|
211
258
|
self.azure_storage_access_key = yml['azure_storage_access_key'] if yml.has_key?("azure_storage_access_key")
|
212
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
|
+
|
213
264
|
# TODO deprecate the other old style config settings. FML.
|
214
265
|
self.aws_access_key_id = yml["aws_access_key"] if yml.has_key?("aws_access_key")
|
215
266
|
self.aws_secret_access_key = yml["aws_access_secret"] if yml.has_key?("aws_access_secret")
|
@@ -238,6 +289,7 @@ module AssetSync
|
|
238
289
|
:aws_access_key_id => aws_access_key_id,
|
239
290
|
:aws_secret_access_key => aws_secret_access_key
|
240
291
|
})
|
292
|
+
options.merge!({:aws_session_token => aws_session_token}) if aws_session_token
|
241
293
|
end
|
242
294
|
options.merge!({:host => fog_host}) if fog_host
|
243
295
|
options.merge!({:port => fog_port}) if fog_port
|
@@ -255,6 +307,8 @@ module AssetSync
|
|
255
307
|
elsif google?
|
256
308
|
if google_json_key_location
|
257
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})
|
258
312
|
else
|
259
313
|
options.merge!({
|
260
314
|
:google_storage_secret_access_key => google_storage_secret_access_key,
|
@@ -269,6 +323,13 @@ module AssetSync
|
|
269
323
|
:azure_storage_access_key => azure_storage_access_key,
|
270
324
|
})
|
271
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
|
+
})
|
272
333
|
else
|
273
334
|
raise ArgumentError, "AssetSync Unknown provider: #{fog_provider} only AWS, Rackspace and Google are supported currently."
|
274
335
|
end
|
data/lib/asset_sync/engine.rb
CHANGED
@@ -23,7 +23,9 @@ module AssetSync
|
|
23
23
|
|
24
24
|
config.aws_access_key_id = ENV['AWS_ACCESS_KEY_ID'] if ENV.has_key?('AWS_ACCESS_KEY_ID')
|
25
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')
|
26
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')
|
27
29
|
config.aws_reduced_redundancy = ENV['AWS_REDUCED_REDUNDANCY'] == true if ENV.has_key?('AWS_REDUCED_REDUNDANCY')
|
28
30
|
|
29
31
|
config.rackspace_username = ENV['RACKSPACE_USERNAME'] if ENV.has_key?('RACKSPACE_USERNAME')
|
@@ -35,6 +37,10 @@ module AssetSync
|
|
35
37
|
config.azure_storage_account_name = ENV['AZURE_STORAGE_ACCOUNT_NAME'] if ENV.has_key?('AZURE_STORAGE_ACCOUNT_NAME')
|
36
38
|
config.azure_storage_access_key = ENV['AZURE_STORAGE_ACCESS_KEY'] if ENV.has_key?('AZURE_STORAGE_ACCESS_KEY')
|
37
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
|
+
|
38
44
|
config.enabled = (ENV['ASSET_SYNC_ENABLED'] == 'true') if ENV.has_key?('ASSET_SYNC_ENABLED')
|
39
45
|
|
40
46
|
config.existing_remote_files = ENV['ASSET_SYNC_EXISTING_REMOTE_FILES'] || "keep"
|
@@ -42,6 +48,8 @@ module AssetSync
|
|
42
48
|
config.gzip_compression = (ENV['ASSET_SYNC_GZIP_COMPRESSION'] == 'true') if ENV.has_key?('ASSET_SYNC_GZIP_COMPRESSION')
|
43
49
|
config.manifest = (ENV['ASSET_SYNC_MANIFEST'] == 'true') if ENV.has_key?('ASSET_SYNC_MANIFEST')
|
44
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')
|
45
53
|
end
|
46
54
|
|
47
55
|
config.prefix = ENV['ASSET_SYNC_PREFIX'] if ENV.has_key?('ASSET_SYNC_PREFIX')
|
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,6 +43,10 @@ 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
|
@@ -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
|
|
@@ -153,7 +195,9 @@ module AssetSync
|
|
153
195
|
|
154
196
|
# region fog_public
|
155
197
|
|
156
|
-
if config.
|
198
|
+
if config.aws? && config.aws_acl
|
199
|
+
file[:acl] = config.aws_acl
|
200
|
+
elsif config.fog_public.use_explicit_value?
|
157
201
|
file[:public] = config.fog_public.to_bool
|
158
202
|
end
|
159
203
|
|
@@ -237,16 +281,32 @@ module AssetSync
|
|
237
281
|
end
|
238
282
|
|
239
283
|
def upload_files
|
240
|
-
# get a fresh list of remote files
|
241
|
-
remote_files = ignore_existing_remote_files? ? [] : get_remote_files
|
242
284
|
# fixes: https://github.com/rumblelabs/asset_sync/issues/19
|
243
285
|
local_files_to_upload = local_files - ignored_files - remote_files + always_upload_files
|
244
286
|
local_files_to_upload = (local_files_to_upload + get_non_fingerprinted(local_files_to_upload)).uniq
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
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
|
250
310
|
end
|
251
311
|
|
252
312
|
if self.config.cdn_distribution_id && files_to_invalidate.any?
|
@@ -255,6 +315,8 @@ module AssetSync
|
|
255
315
|
data = cdn.post_invalidation(self.config.cdn_distribution_id, files_to_invalidate)
|
256
316
|
log "Invalidation id: #{data.body["Id"]}"
|
257
317
|
end
|
318
|
+
|
319
|
+
update_remote_file_list_cache(local_files_to_upload)
|
258
320
|
end
|
259
321
|
|
260
322
|
def sync
|
data/lib/asset_sync/version.rb
CHANGED
@@ -5,7 +5,7 @@ module AssetSync
|
|
5
5
|
|
6
6
|
# Commandline options can be defined here using Thor-like options:
|
7
7
|
class_option :use_yml, :type => :boolean, :default => false, :desc => "Use YML file instead of Rails Initializer"
|
8
|
-
class_option :provider, :type => :string, :default => "AWS", :desc => "Generate with support for 'AWS', 'Rackspace', 'Google', or '
|
8
|
+
class_option :provider, :type => :string, :default => "AWS", :desc => "Generate with support for 'AWS', 'Rackspace', 'Google', 'AzureRM', or 'Backblaze'"
|
9
9
|
|
10
10
|
def self.source_root
|
11
11
|
@source_root ||= File.join(File.dirname(__FILE__), 'templates')
|
@@ -27,6 +27,10 @@ module AssetSync
|
|
27
27
|
options[:provider] == 'AzureRM'
|
28
28
|
end
|
29
29
|
|
30
|
+
def backblaze?
|
31
|
+
options[:provider] == 'Backblaze'
|
32
|
+
end
|
33
|
+
|
30
34
|
def aws_access_key_id
|
31
35
|
"<%= ENV['AWS_ACCESS_KEY_ID'] %>"
|
32
36
|
end
|
@@ -35,6 +39,10 @@ module AssetSync
|
|
35
39
|
"<%= ENV['AWS_SECRET_ACCESS_KEY'] %>"
|
36
40
|
end
|
37
41
|
|
42
|
+
def aws_session_token
|
43
|
+
"<%= ENV['AWS_SESSION_TOKEN'] %>"
|
44
|
+
end
|
45
|
+
|
38
46
|
def google_storage_access_key_id
|
39
47
|
"<%= ENV['GOOGLE_STORAGE_ACCESS_KEY_ID'] %>"
|
40
48
|
end
|
@@ -59,6 +67,18 @@ module AssetSync
|
|
59
67
|
"<%= ENV['AZURE_STORAGE_ACCESS_KEY'] %>"
|
60
68
|
end
|
61
69
|
|
70
|
+
def b2_key_id
|
71
|
+
"<%= ENV['B2_KEY_ID'] %>"
|
72
|
+
end
|
73
|
+
|
74
|
+
def b2_key_token
|
75
|
+
"<%= ENV['B2_KEY_TOKEN'] %>"
|
76
|
+
end
|
77
|
+
|
78
|
+
def b2_bucket_id
|
79
|
+
"<%= ENV['B2_BUCKET_ID'] %>"
|
80
|
+
end
|
81
|
+
|
62
82
|
def app_name
|
63
83
|
@app_name ||= Rails.application.is_a?(Rails::Application) && Rails.application.class.name.sub(/::Application$/, "").downcase
|
64
84
|
end
|
@@ -4,12 +4,18 @@ if defined?(AssetSync)
|
|
4
4
|
config.fog_provider = 'AWS'
|
5
5
|
config.aws_access_key_id = ENV['AWS_ACCESS_KEY_ID']
|
6
6
|
config.aws_secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
|
7
|
+
config.aws_session_token = ENV['AWS_SESSION_TOKEN'] if ENV.key?('AWS_SESSION_TOKEN')
|
7
8
|
# To use AWS reduced redundancy storage.
|
8
9
|
# config.aws_reduced_redundancy = true
|
9
10
|
#
|
10
11
|
# Change AWS signature version. Default is 4
|
11
12
|
# config.aws_signature_version = 4
|
12
13
|
#
|
14
|
+
# Change canned ACL of uploaded object. Default is unset. Will override fog_public if set.
|
15
|
+
# Choose from: private | public-read | public-read-write | aws-exec-read |
|
16
|
+
# authenticated-read | bucket-owner-read | bucket-owner-full-control
|
17
|
+
# config.aws_acl = nil
|
18
|
+
#
|
13
19
|
# Change host option in fog (only if you need to)
|
14
20
|
# config.fog_host = "s3.amazonaws.com"
|
15
21
|
#
|
@@ -34,6 +40,12 @@ if defined?(AssetSync)
|
|
34
40
|
config.azure_storage_account_name = ENV['AZURE_STORAGE_ACCOUNT_NAME']
|
35
41
|
config.azure_storage_access_key = ENV['AZURE_STORAGE_ACCESS_KEY']
|
36
42
|
|
43
|
+
<%- elsif backblaze? -%>
|
44
|
+
config.fog_provider = 'Backblaze'
|
45
|
+
config.b2_key_id = ENV['B2_KEY_ID']
|
46
|
+
config.b2_key_token = ENV['B2_KEY_TOKEN']
|
47
|
+
config.b2_bucket_id = ENV['B2_BUCKET_ID']
|
48
|
+
|
37
49
|
# config.fog_directory specifies container name of Azure Blob storage
|
38
50
|
<%- end -%>
|
39
51
|
config.fog_directory = ENV['FOG_DIRECTORY']
|
@@ -63,6 +75,12 @@ if defined?(AssetSync)
|
|
63
75
|
# Upload the manifest file also.
|
64
76
|
# config.include_manifest = false
|
65
77
|
#
|
78
|
+
# Upload files concurrently
|
79
|
+
# config.concurrent_uploads = false
|
80
|
+
#
|
81
|
+
# Path to cache file to skip scanning remote
|
82
|
+
# config.remote_file_list_cache_file_path = './.asset_sync_remote_file_list_cache.json'
|
83
|
+
#
|
66
84
|
# Fail silently. Useful for environments such as Heroku
|
67
85
|
# config.fail_silently = true
|
68
86
|
#
|
@@ -10,11 +10,16 @@ defaults: &defaults
|
|
10
10
|
# Change AWS signature version. Default is 4
|
11
11
|
# aws_signature_version: 4
|
12
12
|
#
|
13
|
+
# Change canned ACL of uploaded object. Default is unset. Will override fog_public if set.
|
14
|
+
# Choose from: private | public-read | public-read-write | aws-exec-read |
|
15
|
+
# authenticated-read | bucket-owner-read | bucket-owner-full-control
|
16
|
+
# aws_acl: null
|
17
|
+
#
|
13
18
|
# Change host option in fog (only if you need to)
|
14
19
|
# fog_host: "s3.amazonaws.com"
|
15
20
|
#
|
16
21
|
# Change port option in fog (only if you need to)
|
17
|
-
#
|
22
|
+
# fog_port: "9000"
|
18
23
|
#
|
19
24
|
# Use http instead of https. Default should be "https" (at least for fog-aws)
|
20
25
|
# fog_scheme: "http"
|
@@ -32,8 +37,13 @@ defaults: &defaults
|
|
32
37
|
fog_provider: 'AzureRM'
|
33
38
|
azure_storage_account_name: "<%= azure_storage_account_name %>"
|
34
39
|
azure_storage_access_key: "<%= azure_storage_access_key %>"
|
35
|
-
|
36
40
|
# fog_directory specifies container name of Azure Blob storage
|
41
|
+
<%- elsif backblaze? -%>
|
42
|
+
fog_provider: Backblaze
|
43
|
+
b2_key_id: "<%= b2_key_id %>"
|
44
|
+
b2_key_token: "<%= b2_key_token %>"
|
45
|
+
b2_bucket_id: "<%= b2_bucket_id %>"
|
46
|
+
# fog_directory specifies container name of Backblaze B2 Bucket
|
37
47
|
<%- end -%>
|
38
48
|
fog_directory: "<%= app_name %>-assets"
|
39
49
|
|
@@ -43,7 +53,7 @@ defaults: &defaults
|
|
43
53
|
# Set `public` option when uploading file depending on value,
|
44
54
|
# Setting to "default" makes asset sync skip setting the option
|
45
55
|
# Possible values: true, false, "default" (default: true)
|
46
|
-
#
|
56
|
+
# fog_public: "true"
|
47
57
|
|
48
58
|
existing_remote_files: keep
|
49
59
|
# To delete existing remote files.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
defaults: &defaults
|
2
|
+
fog_provider: "Backblaze"
|
3
|
+
b2_key_id: 'xxxx'
|
4
|
+
b2_key_token: 'zzzz'
|
5
|
+
b2_bucket_id: '1234'
|
6
|
+
|
7
|
+
development:
|
8
|
+
<<: *defaults
|
9
|
+
fog_directory: "rails_app_development"
|
10
|
+
existing_remote_files: keep
|
11
|
+
|
12
|
+
test:
|
13
|
+
<<: *defaults
|
14
|
+
fog_directory: "rails_app_test"
|
15
|
+
existing_remote_files: keep
|
16
|
+
|
17
|
+
production:
|
18
|
+
<<: *defaults
|
19
|
+
fog_directory: "rails_app_production"
|
20
|
+
existing_remote_files: delete
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require "fog/backblaze"
|
3
|
+
|
4
|
+
def bucket(name)
|
5
|
+
options = {
|
6
|
+
:provider => 'Backblaze',
|
7
|
+
:b2_key_id => ENV['B2_KEY_ID'],
|
8
|
+
:b2_key_token => ENV['B2_KEY_TOKEN'],
|
9
|
+
:b2_bucket_id => ENV['B2_BUCKET_ID']
|
10
|
+
}
|
11
|
+
options.merge!({ :environment => ENV['FOG_REGION'] }) if ENV.has_key?('FOG_REGION')
|
12
|
+
|
13
|
+
connection = Fog::Storage.new(options)
|
14
|
+
connection.directories.get(ENV['FOG_DIRECTORY'])
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute(command)
|
18
|
+
app_path = File.expand_path("../../dummy_app", __FILE__)
|
19
|
+
Dir.chdir app_path
|
20
|
+
`#{command}`
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "AssetSync" do
|
24
|
+
|
25
|
+
before(:each) do
|
26
|
+
@prefix = SecureRandom.hex(6)
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:app_js_regex){
|
30
|
+
/#{@prefix}\/application-[a-zA-Z0-9]*.js$/
|
31
|
+
}
|
32
|
+
|
33
|
+
let(:app_js_gz_regex){
|
34
|
+
/#{@prefix}\/application-[a-zA-Z0-9]*.js.gz$/
|
35
|
+
}
|
36
|
+
|
37
|
+
let(:files){ bucket(@prefix).files }
|
38
|
+
|
39
|
+
|
40
|
+
after(:each) do
|
41
|
+
@directory = bucket(@prefix)
|
42
|
+
@directory.files.each do |f|
|
43
|
+
f.destroy
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "sync" do
|
48
|
+
execute "rake ASSET_SYNC_PREFIX=#{@prefix} assets:precompile"
|
49
|
+
|
50
|
+
files = bucket(@prefix).files
|
51
|
+
|
52
|
+
app_js = files.select{ |f| f.key =~ app_js_regex }.first
|
53
|
+
expect(app_js.content_type).to eq("application/javascript")
|
54
|
+
|
55
|
+
app_js_gz = files.select{ |f| f.key =~ app_js_gz_regex }.first
|
56
|
+
expect(app_js_gz.content_type).to eq("application/javascript")
|
57
|
+
expect(app_js_gz.content_encoding).to eq("gzip")
|
58
|
+
end
|
59
|
+
|
60
|
+
it "sync with enabled=false" do
|
61
|
+
execute "rake ASSET_SYNC_PREFIX=#{@prefix} ASSET_SYNC_ENABLED=false assets:precompile"
|
62
|
+
expect(bucket(@prefix).files.size).to eq(0)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "sync with gzip_compression=true" do
|
66
|
+
execute "rake ASSET_SYNC_PREFIX=#{@prefix} ASSET_SYNC_GZIP_COMPRESSION=true assets:precompile"
|
67
|
+
# bucket(@prefix).files.size.should == 3
|
68
|
+
|
69
|
+
app_js_path = files.select{ |f| f.key =~ app_js_regex }.first
|
70
|
+
app_js = files.get( app_js_path.key )
|
71
|
+
expect(app_js.content_type).to eq("application/javascript")
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|