sq-asset_sync 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +42 -0
  5. data/Appraisals +15 -0
  6. data/CHANGELOG.md +738 -0
  7. data/Gemfile +7 -0
  8. data/README.md +466 -0
  9. data/Rakefile +44 -0
  10. data/asset_sync.gemspec +38 -0
  11. data/docs/heroku.md +36 -0
  12. data/gemfiles/rails_3.1.gemfile +10 -0
  13. data/gemfiles/rails_3.2.gemfile +10 -0
  14. data/gemfiles/rails_4.0.gemfile +10 -0
  15. data/gemfiles/rails_4.1.gemfile +10 -0
  16. data/kochiku.yml +7 -0
  17. data/lib/asset_sync.rb +15 -0
  18. data/lib/asset_sync/asset_sync.rb +73 -0
  19. data/lib/asset_sync/config.rb +226 -0
  20. data/lib/asset_sync/engine.rb +53 -0
  21. data/lib/asset_sync/multi_mime.rb +16 -0
  22. data/lib/asset_sync/railtie.rb +5 -0
  23. data/lib/asset_sync/storage.rb +291 -0
  24. data/lib/asset_sync/version.rb +3 -0
  25. data/lib/generators/asset_sync/install_generator.rb +67 -0
  26. data/lib/generators/asset_sync/templates/asset_sync.rb +41 -0
  27. data/lib/generators/asset_sync/templates/asset_sync.yml +43 -0
  28. data/lib/tasks/asset_sync.rake +30 -0
  29. data/script/ci +31 -0
  30. data/spec/dummy_app/Rakefile +30 -0
  31. data/spec/dummy_app/app/assets/javascripts/application.js +1 -0
  32. data/spec/fixtures/aws_with_yml/config/asset_sync.yml +25 -0
  33. data/spec/fixtures/google_with_yml/config/asset_sync.yml +19 -0
  34. data/spec/fixtures/rackspace_with_yml/config/asset_sync.yml +20 -0
  35. data/spec/fixtures/with_invalid_yml/config/asset_sync.yml +24 -0
  36. data/spec/integration/aws_integration_spec.rb +77 -0
  37. data/spec/spec_helper.rb +64 -0
  38. data/spec/unit/asset_sync_spec.rb +257 -0
  39. data/spec/unit/google_spec.rb +142 -0
  40. data/spec/unit/multi_mime_spec.rb +48 -0
  41. data/spec/unit/rackspace_spec.rb +90 -0
  42. data/spec/unit/railsless_spec.rb +72 -0
  43. data/spec/unit/storage_spec.rb +244 -0
  44. metadata +248 -0
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ require 'appraisal'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+ require 'bundler/gem_tasks'
9
+ require 'rspec/core/rake_task'
10
+ require 'sq/gem_tasks'
11
+
12
+ namespace :spec do
13
+ RSpec::Core::RakeTask.new(:unit) do |spec|
14
+ spec.pattern = 'spec/unit/*_spec.rb'
15
+ spec.rspec_opts = ['--backtrace']
16
+
17
+ rbx = defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
18
+ jruby = defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
19
+ if RUBY_VERSION == '1.8.7' && !(rbx || jruby)
20
+ spec.rcov = true
21
+ spec.rcov_opts = %w{--exclude gems\/,spec\/}
22
+ end
23
+ end
24
+ RSpec::Core::RakeTask.new(:integration) do |spec|
25
+ spec.pattern = 'spec/integration/*_spec.rb'
26
+ spec.rspec_opts = ['--backtrace']
27
+ end
28
+ desc "run spec:unit and spec:integration tasks"
29
+ task :all do
30
+ Rake::Task['spec:unit'].execute
31
+
32
+ # Travis CI does not expose encrypted ENV variables for pull requests, so
33
+ # do not run integration specs
34
+ # http://about.travis-ci.org/docs/user/build-configuration/#Set-environment-variables
35
+ #
36
+ pull_request = ENV['TRAVIS_PULL_REQUEST'] == 'true'
37
+ ci_build = ENV['TRAVIS'] == 'true'
38
+ if !ci_build || (ci_build && pull_request)
39
+ Rake::Task['spec:integration'].execute
40
+ end
41
+ end
42
+ end
43
+
44
+ task :default => 'spec:all'
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ require "asset_sync/version"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "sq-asset_sync"
8
+ s.version = AssetSync::VERSION
9
+ s.date = "2013-08-26"
10
+ s.platform = Gem::Platform::RUBY
11
+ s.authors = ["Simon Hamilton", "David Rice", "Phil McClure", "Toby Osbourn"]
12
+ s.email = ["shamilton@rumblelabs.com", "me@davidjrice.co.uk", "pmcclure@rumblelabs.com", "tosbourn@rumblelabs.com"]
13
+ s.homepage = "https://github.com/rumblelabs/asset_sync"
14
+ s.summary = %q{Synchronises Assets in a Rails 3 application and Amazon S3/Cloudfront and Rackspace Cloudfiles}
15
+ s.description = %q{After you run assets:precompile your compiled assets will be synchronised with your S3 bucket.}
16
+
17
+ s.license = 'MIT'
18
+
19
+ s.rubyforge_project = "asset_sync"
20
+
21
+ s.add_dependency('fog-core', ">= 1.8.0")
22
+ s.add_dependency('unf')
23
+ s.add_dependency('activemodel')
24
+ s.add_dependency('activesupport')
25
+
26
+ s.add_development_dependency "rspec"
27
+ s.add_development_dependency "fog-aws"
28
+ s.add_development_dependency "bundler"
29
+ s.add_development_dependency "jeweler"
30
+
31
+ s.add_development_dependency "uglifier"
32
+ s.add_development_dependency "appraisal"
33
+
34
+ s.files = `git ls-files`.split("\n")
35
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
36
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
37
+ s.require_paths = ["lib"]
38
+ end
@@ -0,0 +1,36 @@
1
+ The following issues are currently present in Heroku if you are not following the steps outlined in the main [README](http://github.com/rumblelabs/asset_sync)
2
+
3
+ ## KNOWN ISSUES (IMPORTANT)
4
+
5
+ We are currently trying to talk with Heroku to iron these out.
6
+
7
+ 1. Will not work on heroku on an application with a *RAILS_ENV* configured as anything other than production
8
+ 2. Will not work on heroku using ENV variables with the configuration as described below, you must hardcode all variables
9
+
10
+ ### 1. RAILS_ENV
11
+
12
+ When you see `rake assets:precompile` during deployment. Heroku is actually running something like
13
+
14
+ env RAILS_ENV=production DATABASE_URL=scheme://user:pass@127.0.0.1/dbname bundle exec rake assets:precompile 2>&1
15
+
16
+ This means the *RAILS_ENV* you have set via *heroku:config* is not used.
17
+
18
+ **Workaround:** you could have just one S3 bucket dedicated to assets and configure `AssetSync` to not delete existing files:
19
+
20
+ AssetSync.configure do |config|
21
+ ...
22
+ config.fog_directory = 'app-assets'
23
+ config.existing_remote_files = "keep"
24
+ end
25
+
26
+ ### 2. ENV varables not available
27
+
28
+ Currently when heroku runs `rake assets:precompile` during deployment. It does not load your Rails application's environment config. This means using any **ENV** variables you could normally depend on are not available. For now you can just run `heroku run rake assets:precompile` after deploy.
29
+
30
+ **Workaround:** you could just hardcode your AWS credentials in the initializer or yml
31
+
32
+ AssetSync.configure do |config|
33
+ config.aws_access_key_id = 'xxx'
34
+ config.aws_secret_access_key = 'xxx'
35
+ config.fog_directory = 'mybucket'
36
+ end
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rcov", :platforms=>:mri_18, :group=>[:development, :test]
6
+ gem "simplecov", :platforms=>[:jruby, :mri_19, :ruby_19, :mri_20, :rbx], :group=>[:development, :test], :require=>false
7
+ gem "jruby-openssl", :platform=>:jruby
8
+ gem "rails", "~> 3.1.0"
9
+
10
+ gemspec :path=>"../"
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rcov", :platforms=>:mri_18, :group=>[:development, :test]
6
+ gem "simplecov", :platforms=>[:jruby, :mri_19, :ruby_19, :mri_20, :rbx], :group=>[:development, :test], :require=>false
7
+ gem "jruby-openssl", :platform=>:jruby
8
+ gem "rails", "~> 3.2.0"
9
+
10
+ gemspec :path=>"../"
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rcov", :platforms=>:mri_18, :group=>[:development, :test]
6
+ gem "simplecov", :platforms=>[:jruby, :mri_19, :ruby_19, :mri_20, :rbx], :group=>[:development, :test], :require=>false
7
+ gem "jruby-openssl", :platform=>:jruby
8
+ gem "rails", "~> 4.0.0"
9
+
10
+ gemspec :path=>"../"
@@ -0,0 +1,10 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rcov", :platforms => :mri_18, :group => [:development, :test]
6
+ gem "simplecov", :platforms => [:jruby, :mri_19, :ruby_19, :mri_20, :rbx], :group => [:development, :test], :require => false
7
+ gem "jruby-openssl", :platform => :jruby
8
+ gem "rails", "~> 4.1.0"
9
+
10
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ ruby:
2
+ - 2.3.0
3
+ test_command: script/ci
4
+ targets:
5
+ - type: spec
6
+ glob: spec/unit/**/*_spec.rb
7
+ workers: 1
@@ -0,0 +1,15 @@
1
+ require 'fog/core'
2
+ require 'active_model'
3
+ require 'active_support/time'
4
+ require 'active_support/time_with_zone'
5
+ require 'active_support/values/time_zone'
6
+ require 'erb'
7
+ require "asset_sync/asset_sync"
8
+ require 'asset_sync/config'
9
+ require 'asset_sync/storage'
10
+ require 'asset_sync/multi_mime'
11
+
12
+ if defined?(Rails)
13
+ require 'asset_sync/railtie'
14
+ require 'asset_sync/engine'
15
+ end
@@ -0,0 +1,73 @@
1
+ module AssetSync
2
+
3
+ class << self
4
+
5
+ def config=(data)
6
+ @config = data
7
+ end
8
+
9
+ def config
10
+ @config ||= Config.new
11
+ @config
12
+ end
13
+
14
+ def reset_config!
15
+ remove_instance_variable :@config if defined?(@config)
16
+ end
17
+
18
+ def configure(&proc)
19
+ @config ||= Config.new
20
+ yield @config
21
+ end
22
+
23
+ def storage
24
+ @storage ||= Storage.new(self.config)
25
+ end
26
+
27
+ def sync
28
+ with_config do
29
+ self.storage.sync
30
+ end
31
+ end
32
+
33
+ def clean
34
+ with_config do
35
+ self.storage.delete_extra_remote_files
36
+ end
37
+ end
38
+
39
+ def with_config(&block)
40
+ return unless AssetSync.enabled?
41
+
42
+ errors = config.valid? ? "" : config.errors.full_messages.join(', ')
43
+
44
+ if !(config && config.valid?)
45
+ if config.fail_silently?
46
+ self.warn(errors)
47
+ else
48
+ raise Config::Invalid.new(errors)
49
+ end
50
+ else
51
+ block.call
52
+ end
53
+ end
54
+
55
+ def warn(msg)
56
+ stderr.puts msg
57
+ end
58
+
59
+ def log(msg)
60
+ stdout.puts msg unless config.log_silently?
61
+ end
62
+
63
+ def enabled?
64
+ config.enabled?
65
+ end
66
+
67
+ # easier to stub
68
+ def stderr ; STDERR ; end
69
+ def stdout ; STDOUT ; end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,226 @@
1
+ module AssetSync
2
+ class Config
3
+ include ActiveModel::Validations
4
+
5
+ class Invalid < StandardError; end
6
+
7
+ # AssetSync
8
+ attr_accessor :existing_remote_files # What to do with your existing remote files? (keep or delete)
9
+ attr_accessor :gzip_compression
10
+ attr_accessor :manifest
11
+ attr_accessor :fail_silently
12
+ attr_accessor :log_silently
13
+ attr_accessor :always_upload
14
+ attr_accessor :ignored_files
15
+ attr_accessor :prefix
16
+ attr_accessor :public_path
17
+ attr_accessor :enabled
18
+ attr_accessor :custom_headers
19
+ attr_accessor :run_on_precompile
20
+ attr_accessor :invalidate
21
+ attr_accessor :cdn_distribution_id
22
+
23
+ # FOG configuration
24
+ attr_accessor :fog_provider # Currently Supported ['AWS', 'Rackspace']
25
+ attr_accessor :fog_directory # e.g. 'the-bucket-name'
26
+ attr_accessor :fog_region # e.g. 'eu-west-1'
27
+
28
+ # Amazon AWS
29
+ attr_accessor :aws_access_key_id, :aws_secret_access_key, :aws_reduced_redundancy, :aws_iam_roles
30
+
31
+ # Rackspace
32
+ attr_accessor :rackspace_username, :rackspace_api_key, :rackspace_auth_url
33
+
34
+ # Google Storage
35
+ attr_accessor :google_storage_secret_access_key, :google_storage_access_key_id
36
+
37
+ validates :existing_remote_files, :inclusion => { :in => %w(keep delete ignore) }
38
+
39
+ validates :fog_provider, :presence => true
40
+ validates :fog_directory, :presence => true
41
+
42
+ validates :aws_access_key_id, :presence => true, :if => proc {aws? && !aws_iam?}
43
+ validates :aws_secret_access_key, :presence => true, :if => proc {aws? && !aws_iam?}
44
+ validates :rackspace_username, :presence => true, :if => :rackspace?
45
+ validates :rackspace_api_key, :presence => true, :if => :rackspace?
46
+ validates :google_storage_secret_access_key, :presence => true, :if => :google?
47
+ validates :google_storage_access_key_id, :presence => true, :if => :google?
48
+
49
+ def initialize
50
+ self.fog_region = nil
51
+ self.existing_remote_files = 'keep'
52
+ self.gzip_compression = false
53
+ self.manifest = false
54
+ self.fail_silently = false
55
+ self.log_silently = true
56
+ self.always_upload = []
57
+ self.ignored_files = []
58
+ self.custom_headers = {}
59
+ self.enabled = true
60
+ self.run_on_precompile = true
61
+ self.cdn_distribution_id = nil
62
+ self.invalidate = []
63
+ load_yml! if defined?(Rails) && yml_exists?
64
+ end
65
+
66
+ def manifest_digest_path
67
+ manifest_path = Dir["#{default_manifest_directory}/manifest-*.json"].try(:last)
68
+ if manifest_path
69
+ "#{assets_prefix}/#{File.basename(manifest_path)}"
70
+ end
71
+ end
72
+
73
+ def manifest_path
74
+ directory =
75
+ Rails.application.config.assets.manifest || default_manifest_directory
76
+ File.join(directory, "manifest.yml")
77
+ end
78
+
79
+ def gzip?
80
+ self.gzip_compression
81
+ end
82
+
83
+ def existing_remote_files?
84
+ ['keep', 'ignore'].include?(self.existing_remote_files)
85
+ end
86
+
87
+ def aws?
88
+ fog_provider =~ /aws/i
89
+ end
90
+
91
+ def aws_rrs?
92
+ aws_reduced_redundancy == true
93
+ end
94
+
95
+ def aws_iam?
96
+ aws_iam_roles == true
97
+ end
98
+
99
+ def fail_silently?
100
+ fail_silently || !enabled?
101
+ end
102
+
103
+ def log_silently?
104
+ ENV['RAILS_GROUPS'] == 'assets' || !!self.log_silently
105
+ end
106
+
107
+ def enabled?
108
+ enabled == true
109
+ end
110
+
111
+ def rackspace?
112
+ fog_provider =~ /rackspace/i
113
+ end
114
+
115
+ def google?
116
+ fog_provider =~ /google/i
117
+ end
118
+
119
+ def yml_exists?
120
+ defined?(Rails.root) ? File.exist?(self.yml_path) : false
121
+ end
122
+
123
+ def yml
124
+ begin
125
+ @yml ||= YAML.load(ERB.new(IO.read(yml_path)).result)[Rails.env] rescue nil || {}
126
+ rescue Psych::SyntaxError
127
+ @yml = {}
128
+ end
129
+ end
130
+
131
+ def yml_path
132
+ Rails.root.join("config", "asset_sync.yml").to_s
133
+ end
134
+
135
+ def assets_prefix
136
+ # Fix for Issue #38 when Rails.config.assets.prefix starts with a slash
137
+ self.prefix || Rails.application.config.assets.prefix.sub(/^\//, '')
138
+ end
139
+
140
+ def public_path
141
+ @public_path || Rails.public_path
142
+ end
143
+
144
+ def load_yml!
145
+ self.enabled = yml["enabled"] if yml.has_key?('enabled')
146
+ self.fog_provider = yml["fog_provider"]
147
+ self.fog_directory = yml["fog_directory"]
148
+ self.fog_region = yml["fog_region"]
149
+ self.aws_access_key_id = yml["aws_access_key_id"]
150
+ self.aws_secret_access_key = yml["aws_secret_access_key"]
151
+ self.aws_reduced_redundancy = yml["aws_reduced_redundancy"]
152
+ self.aws_iam_roles = yml["aws_iam_roles"]
153
+ self.rackspace_username = yml["rackspace_username"]
154
+ self.rackspace_auth_url = yml["rackspace_auth_url"] if yml.has_key?("rackspace_auth_url")
155
+ self.rackspace_api_key = yml["rackspace_api_key"]
156
+ self.google_storage_secret_access_key = yml["google_storage_secret_access_key"]
157
+ self.google_storage_access_key_id = yml["google_storage_access_key_id"]
158
+ self.existing_remote_files = yml["existing_remote_files"] if yml.has_key?("existing_remote_files")
159
+ self.gzip_compression = yml["gzip_compression"] if yml.has_key?("gzip_compression")
160
+ self.manifest = yml["manifest"] if yml.has_key?("manifest")
161
+ self.fail_silently = yml["fail_silently"] if yml.has_key?("fail_silently")
162
+ self.always_upload = yml["always_upload"] if yml.has_key?("always_upload")
163
+ self.ignored_files = yml["ignored_files"] if yml.has_key?("ignored_files")
164
+ self.custom_headers = yml["custom_headers"] if yml.has_key?("custom_headers")
165
+ self.run_on_precompile = yml["run_on_precompile"] if yml.has_key?("run_on_precompile")
166
+ self.invalidate = yml["invalidate"] if yml.has_key?("invalidate")
167
+ self.cdn_distribution_id = yml['cdn_distribution_id'] if yml.has_key?("cdn_distribution_id")
168
+
169
+ # TODO deprecate the other old style config settings. FML.
170
+ self.aws_access_key_id = yml["aws_access_key"] if yml.has_key?("aws_access_key")
171
+ self.aws_secret_access_key = yml["aws_access_secret"] if yml.has_key?("aws_access_secret")
172
+ self.fog_directory = yml["aws_bucket"] if yml.has_key?("aws_bucket")
173
+ self.fog_region = yml["aws_region"] if yml.has_key?("aws_region")
174
+
175
+ # TODO deprecate old style config settings
176
+ self.aws_access_key_id = yml["access_key_id"] if yml.has_key?("access_key_id")
177
+ self.aws_secret_access_key = yml["secret_access_key"] if yml.has_key?("secret_access_key")
178
+ self.fog_directory = yml["bucket"] if yml.has_key?("bucket")
179
+ self.fog_region = yml["region"] if yml.has_key?("region")
180
+
181
+ self.public_path = yml["public_path"] if yml.has_key?("public_path")
182
+ end
183
+
184
+
185
+ def fog_options
186
+ options = { :provider => fog_provider }
187
+ if aws?
188
+ if aws_iam?
189
+ options.merge!({
190
+ :use_iam_profile => true
191
+ })
192
+ else
193
+ options.merge!({
194
+ :aws_access_key_id => aws_access_key_id,
195
+ :aws_secret_access_key => aws_secret_access_key
196
+ })
197
+ end
198
+ elsif rackspace?
199
+ options.merge!({
200
+ :rackspace_username => rackspace_username,
201
+ :rackspace_api_key => rackspace_api_key
202
+ })
203
+ options.merge!({
204
+ :rackspace_region => fog_region
205
+ }) if fog_region
206
+ options.merge!({ :rackspace_auth_url => rackspace_auth_url }) if rackspace_auth_url
207
+ elsif google?
208
+ options.merge!({
209
+ :google_storage_secret_access_key => google_storage_secret_access_key,
210
+ :google_storage_access_key_id => google_storage_access_key_id
211
+ })
212
+ else
213
+ raise ArgumentError, "AssetSync Unknown provider: #{fog_provider} only AWS, Rackspace and Google are supported currently."
214
+ end
215
+
216
+ options.merge!({:region => fog_region}) if fog_region && !rackspace?
217
+ return options
218
+ end
219
+
220
+ private
221
+
222
+ def default_manifest_directory
223
+ File.join(Rails.public_path, assets_prefix)
224
+ end
225
+ end
226
+ end