sq-asset_sync 2.0.0

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