asset_sync 2.8.1 → 2.15.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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