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.
@@ -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
- attr_writer :public_path
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, :aws_secret_access_key, :aws_reduced_redundancy, :aws_iam_roles, :aws_signature_version
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 ||= ::YAML.load(::ERB.new(IO.read(yml_path)).result)[::Rails.env] || {}
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 = yml["custom_headers"] if yml.has_key?("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
@@ -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')
@@ -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,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 = YAML.load(IO.read(self.config.manifest_path))
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
- 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
 
@@ -153,7 +195,9 @@ module AssetSync
153
195
 
154
196
  # region fog_public
155
197
 
156
- if config.fog_public.use_explicit_value?
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
- # Upload new files
247
- local_files_to_upload.each do |f|
248
- next unless File.file? "#{path}/#{f}" # Only files.
249
- upload_file f
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AssetSync
4
- VERSION = "2.8.1".freeze
4
+ VERSION = "2.15.2"
5
5
  end
@@ -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 'AzureRM"
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
- # config.fog_port = "9000"
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
- # config.fog_public = true
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