multi_sync 0.0.1
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 +7 -0
- data/.document +3 -0
- data/.gitignore +20 -0
- data/.rubocop.yml +26 -0
- data/.travis.yml +24 -0
- data/.yardopts +6 -0
- data/CHANGELOG.md +0 -0
- data/CONTRIBUTING.md +44 -0
- data/Gemfile +17 -0
- data/LICENSE.md +20 -0
- data/README.md +134 -0
- data/Rakefile +45 -0
- data/examples/custom.rb +34 -0
- data/examples/rails-3-2/.gitignore +15 -0
- data/examples/rails-3-2/Gemfile +43 -0
- data/examples/rails-3-2/README.rdoc +261 -0
- data/examples/rails-3-2/Rakefile +7 -0
- data/examples/rails-3-2/app/assets/images/rails.png +0 -0
- data/examples/rails-3-2/app/assets/javascripts/application.js +15 -0
- data/examples/rails-3-2/app/assets/stylesheets/application.css +13 -0
- data/examples/rails-3-2/app/controllers/application_controller.rb +3 -0
- data/examples/rails-3-2/app/helpers/application_helper.rb +2 -0
- data/examples/rails-3-2/app/mailers/.gitkeep +0 -0
- data/examples/rails-3-2/app/models/.gitkeep +0 -0
- data/examples/rails-3-2/app/views/layouts/application.html.erb +14 -0
- data/examples/rails-3-2/config.ru +4 -0
- data/examples/rails-3-2/config/application.rb +62 -0
- data/examples/rails-3-2/config/asset_sync.yml +21 -0
- data/examples/rails-3-2/config/boot.rb +6 -0
- data/examples/rails-3-2/config/database.yml +25 -0
- data/examples/rails-3-2/config/environment.rb +5 -0
- data/examples/rails-3-2/config/environments/development.rb +37 -0
- data/examples/rails-3-2/config/environments/production.rb +67 -0
- data/examples/rails-3-2/config/environments/test.rb +37 -0
- data/examples/rails-3-2/config/initializers/backtrace_silencers.rb +7 -0
- data/examples/rails-3-2/config/initializers/inflections.rb +15 -0
- data/examples/rails-3-2/config/initializers/mime_types.rb +5 -0
- data/examples/rails-3-2/config/initializers/multi_sync.rb +29 -0
- data/examples/rails-3-2/config/initializers/secret_token.rb +7 -0
- data/examples/rails-3-2/config/initializers/session_store.rb +8 -0
- data/examples/rails-3-2/config/initializers/wrap_parameters.rb +14 -0
- data/examples/rails-3-2/config/locales/en.yml +5 -0
- data/examples/rails-3-2/config/routes.rb +58 -0
- data/examples/rails-3-2/db/seeds.rb +7 -0
- data/examples/rails-3-2/lib/assets/.gitkeep +0 -0
- data/examples/rails-3-2/lib/tasks/.gitkeep +0 -0
- data/examples/rails-3-2/log/.gitkeep +0 -0
- data/examples/rails-3-2/public/404.html +26 -0
- data/examples/rails-3-2/public/422.html +26 -0
- data/examples/rails-3-2/public/500.html +25 -0
- data/examples/rails-3-2/public/favicon.ico +0 -0
- data/examples/rails-3-2/public/index.html +241 -0
- data/examples/rails-3-2/public/robots.txt +5 -0
- data/examples/rails-3-2/script/rails +6 -0
- data/examples/rails-3-2/test/fixtures/.gitkeep +0 -0
- data/examples/rails-3-2/test/functional/.gitkeep +0 -0
- data/examples/rails-3-2/test/integration/.gitkeep +0 -0
- data/examples/rails-3-2/test/performance/browsing_test.rb +12 -0
- data/examples/rails-3-2/test/test_helper.rb +13 -0
- data/examples/rails-3-2/test/unit/.gitkeep +0 -0
- data/examples/rails-4-0/.gitignore +16 -0
- data/examples/rails-4-0/Gemfile +50 -0
- data/examples/rails-4-0/README.rdoc +28 -0
- data/examples/rails-4-0/Rakefile +6 -0
- data/examples/rails-4-0/app/assets/images/.keep +0 -0
- data/examples/rails-4-0/app/assets/javascripts/application.js +16 -0
- data/examples/rails-4-0/app/assets/stylesheets/application.css +13 -0
- data/examples/rails-4-0/app/controllers/application_controller.rb +5 -0
- data/examples/rails-4-0/app/controllers/concerns/.keep +0 -0
- data/examples/rails-4-0/app/helpers/application_helper.rb +2 -0
- data/examples/rails-4-0/app/mailers/.keep +0 -0
- data/examples/rails-4-0/app/models/.keep +0 -0
- data/examples/rails-4-0/app/models/concerns/.keep +0 -0
- data/examples/rails-4-0/app/views/layouts/application.html.erb +14 -0
- data/examples/rails-4-0/config.ru +4 -0
- data/examples/rails-4-0/config/application.rb +24 -0
- data/examples/rails-4-0/config/asset_sync.yml +21 -0
- data/examples/rails-4-0/config/boot.rb +4 -0
- data/examples/rails-4-0/config/database.yml +25 -0
- data/examples/rails-4-0/config/environment.rb +5 -0
- data/examples/rails-4-0/config/environments/development.rb +29 -0
- data/examples/rails-4-0/config/environments/production.rb +80 -0
- data/examples/rails-4-0/config/environments/test.rb +36 -0
- data/examples/rails-4-0/config/initializers/backtrace_silencers.rb +7 -0
- data/examples/rails-4-0/config/initializers/filter_parameter_logging.rb +4 -0
- data/examples/rails-4-0/config/initializers/inflections.rb +16 -0
- data/examples/rails-4-0/config/initializers/mime_types.rb +5 -0
- data/examples/rails-4-0/config/initializers/multi_sync.rb +29 -0
- data/examples/rails-4-0/config/initializers/secret_token.rb +12 -0
- data/examples/rails-4-0/config/initializers/session_store.rb +3 -0
- data/examples/rails-4-0/config/initializers/wrap_parameters.rb +14 -0
- data/examples/rails-4-0/config/locales/en.yml +23 -0
- data/examples/rails-4-0/config/routes.rb +56 -0
- data/examples/rails-4-0/db/seeds.rb +7 -0
- data/examples/rails-4-0/lib/assets/.keep +0 -0
- data/examples/rails-4-0/lib/tasks/.keep +0 -0
- data/examples/rails-4-0/log/.keep +0 -0
- data/examples/rails-4-0/public/404.html +58 -0
- data/examples/rails-4-0/public/422.html +58 -0
- data/examples/rails-4-0/public/500.html +57 -0
- data/examples/rails-4-0/public/favicon.ico +0 -0
- data/examples/rails-4-0/public/robots.txt +5 -0
- data/examples/rails-4-0/test/controllers/.keep +0 -0
- data/examples/rails-4-0/test/fixtures/.keep +0 -0
- data/examples/rails-4-0/test/helpers/.keep +0 -0
- data/examples/rails-4-0/test/integration/.keep +0 -0
- data/examples/rails-4-0/test/mailers/.keep +0 -0
- data/examples/rails-4-0/test/models/.keep +0 -0
- data/examples/rails-4-0/test/test_helper.rb +15 -0
- data/gemfiles/middleman-3.1.x.gemfile +5 -0
- data/gemfiles/rails-3.2.x.gemfile +5 -0
- data/gemfiles/rails-4.0.x.gemfile +5 -0
- data/lib/multi_sync.rb +92 -0
- data/lib/multi_sync/client.rb +243 -0
- data/lib/multi_sync/configuration.rb +36 -0
- data/lib/multi_sync/environment.rb +37 -0
- data/lib/multi_sync/extensions/middleman.rb +22 -0
- data/lib/multi_sync/extensions/rails.rb +20 -0
- data/lib/multi_sync/extensions/rails/asset_sync.rb +44 -0
- data/lib/multi_sync/extensions/rails/railtie.rb +11 -0
- data/lib/multi_sync/logging.rb +60 -0
- data/lib/multi_sync/mixins/log_helper.rb +9 -0
- data/lib/multi_sync/mixins/pluralize_helper.rb +15 -0
- data/lib/multi_sync/resource.rb +84 -0
- data/lib/multi_sync/resources/local_resource.rb +30 -0
- data/lib/multi_sync/resources/remote_resource.rb +32 -0
- data/lib/multi_sync/source.rb +40 -0
- data/lib/multi_sync/sources/local_source.rb +17 -0
- data/lib/multi_sync/sources/manifest_source.rb +82 -0
- data/lib/multi_sync/target.rb +34 -0
- data/lib/multi_sync/targets/aws_target.rb +80 -0
- data/lib/multi_sync/targets/local_target.rb +58 -0
- data/lib/multi_sync/version.rb +3 -0
- data/lib/tasks/multi_sync_rails.rake +15 -0
- data/multi_sync.gemspec +35 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/celluloid.rb +14 -0
- data/spec/support/fakefs.rb +7 -0
- data/spec/support/fog.rb +25 -0
- data/spec/support/pry.rb +6 -0
- data/spec/support/timecop.rb +2 -0
- data/spec/unit/multi_sync/client_spec.rb +301 -0
- data/spec/unit/multi_sync/configuration_spec.rb +92 -0
- data/spec/unit/multi_sync/resources/local_resource_spec.rb +55 -0
- data/spec/unit/multi_sync/sources/local_source_spec.rb +48 -0
- data/spec/unit/multi_sync/sources/manifest_source_spec.rb +57 -0
- data/spec/unit/multi_sync/targets/aws_target_spec.rb +60 -0
- data/spec/unit/multi_sync/targets/local_target_spec.rb +41 -0
- data/spec/unit/multi_sync_spec.rb +55 -0
- metadata +377 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module MultiSync
|
|
2
|
+
module Extensions
|
|
3
|
+
class AssetSync
|
|
4
|
+
class << self
|
|
5
|
+
def asset_sync_yml_exists?
|
|
6
|
+
::Rails.root.nil? ? false : File.exist?(asset_sync_yml_path)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def asset_sync_yml_path
|
|
10
|
+
::Rails.root.join('config', 'asset_sync.yml')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def asset_sync_yml
|
|
14
|
+
@asset_sync_yml ||= YAML.load_file(asset_sync_yml_path)[MultiSync.env]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def check_and_migrate
|
|
18
|
+
return unless self.asset_sync_yml_exists?
|
|
19
|
+
MultiSync.info 'AssetSync YAML file found, migrating options...'
|
|
20
|
+
|
|
21
|
+
MultiSync.source(:rails,
|
|
22
|
+
type: :manifest,
|
|
23
|
+
source_dir: MultiSync::Extensions::Rails.source_dir
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
MultiSync.target(:assets,
|
|
27
|
+
type: asset_sync_yml['fog_provider'],
|
|
28
|
+
target_dir: asset_sync_yml['fog_directory'],
|
|
29
|
+
destination_dir: MultiSync::Extensions::Rails.destination_dir,
|
|
30
|
+
credentials: {
|
|
31
|
+
region: asset_sync_yml['region'],
|
|
32
|
+
aws_access_key_id: asset_sync_yml['aws_access_key_id'],
|
|
33
|
+
aws_secret_access_key: asset_sync_yml['aws_secret_access_key'],
|
|
34
|
+
path_style: asset_sync_yml['path_style']
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
MultiSync.delete_abandoned_files = asset_sync_yml['existing_remote_files'] == 'delete'
|
|
39
|
+
MultiSync.run_on_build = asset_sync_yml['run_on_precompile']
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
|
|
3
|
+
module MultiSync
|
|
4
|
+
module Logging
|
|
5
|
+
MUTEX = Mutex.new
|
|
6
|
+
|
|
7
|
+
def logger
|
|
8
|
+
@logger || initialize_logger
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def logger=(new_logger)
|
|
12
|
+
@logger = new_logger ? new_logger : Logger.new('/dev/null')
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def status_logger
|
|
16
|
+
@status_logger || initialize_status_logger
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def status_logger=(new_status_logger)
|
|
20
|
+
@status_logger = new_status_logger ? new_status_logger : nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def say_status(status, message, log_status = true)
|
|
24
|
+
return if status_logger.nil?
|
|
25
|
+
|
|
26
|
+
if defined?(Thor) && status_logger.is_a?(Thor)
|
|
27
|
+
MUTEX.synchronize do
|
|
28
|
+
status_logger.say_status status, message, log_status
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def log(message, level = :debug)
|
|
34
|
+
# We're in verbose mode so disable all non-info logs
|
|
35
|
+
return if !MultiSync.verbose && level != :info
|
|
36
|
+
|
|
37
|
+
MUTEX.synchronize do
|
|
38
|
+
logger.send(level, message)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Create methods for the different shorthand log methods
|
|
43
|
+
[:info, :debug, :warn, :error].each do |log_method|
|
|
44
|
+
# Shorthand log method
|
|
45
|
+
define_method log_method do |message|
|
|
46
|
+
send(:log, message, log_method)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def initialize_logger
|
|
53
|
+
@logger = ::Logger.new(STDOUT)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def initialize_status_logger
|
|
57
|
+
@status_logger = nil
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module MultiSync
|
|
2
|
+
module Mixins
|
|
3
|
+
module PluralizeHelper
|
|
4
|
+
def pluralize(n, singular, plural = nil, prefix = true)
|
|
5
|
+
if n == 1
|
|
6
|
+
(prefix ? '1 ' : '') + singular
|
|
7
|
+
elsif plural
|
|
8
|
+
(prefix ? "#{n} " : '') + plural
|
|
9
|
+
else
|
|
10
|
+
(prefix ? "#{n} " : '') + "#{singular}s"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require 'virtus'
|
|
2
|
+
require 'pathname'
|
|
3
|
+
require 'digest/md5'
|
|
4
|
+
require 'multi_sync/mixins/log_helper'
|
|
5
|
+
|
|
6
|
+
module MultiSync
|
|
7
|
+
class Resource
|
|
8
|
+
include Virtus
|
|
9
|
+
include Comparable
|
|
10
|
+
include MultiSync::Mixins::LogHelper
|
|
11
|
+
|
|
12
|
+
attribute :file, File
|
|
13
|
+
attribute :path_with_root, Pathname
|
|
14
|
+
attribute :path_without_root, Pathname
|
|
15
|
+
|
|
16
|
+
attribute :etag, String
|
|
17
|
+
attribute :mtime, Time
|
|
18
|
+
attribute :content_length, Numeric
|
|
19
|
+
attribute :content_type, String
|
|
20
|
+
attribute :digest, String
|
|
21
|
+
|
|
22
|
+
AWS_ATTRIBUTES = [{
|
|
23
|
+
name: :storage_class,
|
|
24
|
+
type: String,
|
|
25
|
+
default_value: 'STANDARD'
|
|
26
|
+
}, {
|
|
27
|
+
name: :acl,
|
|
28
|
+
type: String,
|
|
29
|
+
default_value: 'public-read'
|
|
30
|
+
}, {
|
|
31
|
+
name: :expires,
|
|
32
|
+
type: String,
|
|
33
|
+
default_value: nil
|
|
34
|
+
}, {
|
|
35
|
+
name: :cache_control,
|
|
36
|
+
type: String,
|
|
37
|
+
default_value: nil
|
|
38
|
+
}, {
|
|
39
|
+
name: :encryption,
|
|
40
|
+
type: String,
|
|
41
|
+
default_value: nil
|
|
42
|
+
}]
|
|
43
|
+
|
|
44
|
+
AWS_ATTRIBUTES.each do |attribute_hash|
|
|
45
|
+
send(:attribute, attribute_hash[:name], attribute_hash[:type])
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Initialize a new Resource object
|
|
49
|
+
#
|
|
50
|
+
# @param options [Hash]
|
|
51
|
+
def initialize(options = {})
|
|
52
|
+
fail(ArgumentError, 'with_root must be present') unless options[:with_root]
|
|
53
|
+
fail(ArgumentError, 'without_root must be present') unless options[:without_root]
|
|
54
|
+
self.path_with_root = options.fetch(:with_root)
|
|
55
|
+
self.path_without_root = options.fetch(:without_root)
|
|
56
|
+
self.etag = options.fetch(:etag, determine_etag)
|
|
57
|
+
self.mtime = options.fetch(:mtime, determine_mtime)
|
|
58
|
+
self.content_length = options.fetch(:content_length, determine_content_length)
|
|
59
|
+
self.content_type = options.fetch(:content_type, determine_content_type)
|
|
60
|
+
self.digest = options.fetch(:digest, '')
|
|
61
|
+
|
|
62
|
+
AWS_ATTRIBUTES.each do |attribute_hash|
|
|
63
|
+
send("#{attribute_hash[:name]}=".to_sym, options.fetch(attribute_hash[:name], attribute_hash[:default_value]))
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def hash
|
|
68
|
+
path_without_root.hash
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def <=>(other)
|
|
72
|
+
path_without_root <=> other.path_without_root
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def ==(other)
|
|
76
|
+
path_without_root == other.path_without_root
|
|
77
|
+
end
|
|
78
|
+
alias_method :eql?, :==
|
|
79
|
+
|
|
80
|
+
def matching_etag?(other)
|
|
81
|
+
etag == other.etag
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'multi_mime'
|
|
2
|
+
require 'multi_sync/resource'
|
|
3
|
+
|
|
4
|
+
module MultiSync
|
|
5
|
+
class LocalResource < Resource
|
|
6
|
+
def body
|
|
7
|
+
File.read(path_with_root.to_s)
|
|
8
|
+
rescue
|
|
9
|
+
return nil
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def determine_etag
|
|
13
|
+
body.nil? ? nil : Digest::MD5.hexdigest(body)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def determine_mtime
|
|
17
|
+
File.mtime(path_with_root.to_s)
|
|
18
|
+
rescue
|
|
19
|
+
return nil
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def determine_content_type
|
|
23
|
+
MultiMime.type_for_path(path_with_root.to_s)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def determine_content_length
|
|
27
|
+
body.nil? ? 0 : Fog::Storage.get_body_size(body)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'multi_sync/resource'
|
|
2
|
+
|
|
3
|
+
module MultiSync
|
|
4
|
+
class RemoteResource < Resource
|
|
5
|
+
def initialize(options = {})
|
|
6
|
+
self.file = options.fetch(:file, nil)
|
|
7
|
+
super(options)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def body
|
|
11
|
+
file.body
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def determine_etag
|
|
15
|
+
file.etag
|
|
16
|
+
rescue NoMethodError # Fog::Storage::Local::File's don't have an etag method :(
|
|
17
|
+
Digest::MD5.hexdigest(File.read(path_with_root))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def determine_mtime
|
|
21
|
+
file.last_modified
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def determine_content_type
|
|
25
|
+
file.content_type
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def determine_content_length
|
|
29
|
+
file.content_length
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'virtus'
|
|
2
|
+
require 'pathname'
|
|
3
|
+
require 'lazily'
|
|
4
|
+
require 'multi_sync/mixins/log_helper'
|
|
5
|
+
|
|
6
|
+
module MultiSync
|
|
7
|
+
class Source
|
|
8
|
+
include Virtus
|
|
9
|
+
include MultiSync::Mixins::LogHelper
|
|
10
|
+
|
|
11
|
+
attribute :targets, Array, default: []
|
|
12
|
+
attribute :source_dir, String
|
|
13
|
+
attribute :source_options, Hash
|
|
14
|
+
attribute :include, String
|
|
15
|
+
attribute :exclude, String
|
|
16
|
+
|
|
17
|
+
# Initialize a new Source object
|
|
18
|
+
#
|
|
19
|
+
# @param options [Hash]
|
|
20
|
+
def initialize(options = {})
|
|
21
|
+
fail(ArgumentError, 'source_dir must be a present') unless options[:source_dir]
|
|
22
|
+
targets.concat([*options.fetch(:targets, [])])
|
|
23
|
+
self.source_dir = options.fetch(:source_dir).to_s
|
|
24
|
+
source_dir << '/' unless source_dir.end_with?('/')
|
|
25
|
+
self.source_dir = Pathname.new(source_dir)
|
|
26
|
+
self.include = options.fetch(:include, '**/*')
|
|
27
|
+
self.exclude = options.fetch(:exclude, nil)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def path_to_local_resource(path, options = {})
|
|
33
|
+
pathname = Pathname.new(path)
|
|
34
|
+
MultiSync::LocalResource.new({
|
|
35
|
+
with_root: pathname,
|
|
36
|
+
without_root: pathname.relative_path_from(source_dir).cleanpath
|
|
37
|
+
}.merge(options).merge(source_options))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'multi_sync/source'
|
|
2
|
+
require 'multi_sync/resources/local_resource'
|
|
3
|
+
|
|
4
|
+
module MultiSync
|
|
5
|
+
class LocalSource < Source
|
|
6
|
+
def files
|
|
7
|
+
files = []
|
|
8
|
+
included_files = Dir.glob(source_dir + include)
|
|
9
|
+
excluded_files = exclude.nil? ? [] : Dir.glob(source_dir + exclude)
|
|
10
|
+
(included_files - excluded_files).lazily.each { |path|
|
|
11
|
+
next if File.directory?(path)
|
|
12
|
+
files << path_to_local_resource(path)
|
|
13
|
+
}
|
|
14
|
+
files
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require 'multi_json'
|
|
3
|
+
require 'multi_sync/source'
|
|
4
|
+
require 'multi_sync/resources/local_resource'
|
|
5
|
+
|
|
6
|
+
module MultiSync
|
|
7
|
+
class ManifestSource < Source
|
|
8
|
+
def files
|
|
9
|
+
files = []
|
|
10
|
+
manifest_hash = {}
|
|
11
|
+
|
|
12
|
+
# ::ActionView::Base has a shortcut to the manifest file
|
|
13
|
+
# otherwise lets hunt down that manifest file!
|
|
14
|
+
if defined?(::ActionView::Base) && ::ActionView::Base.respond_to?(:assets_manifest)
|
|
15
|
+
manifest_hash = ::ActionView::Base.assets_manifest.files
|
|
16
|
+
else
|
|
17
|
+
manifest_path = locate_manifest(source_dir)
|
|
18
|
+
manifest_hash = parse_manifest(manifest_path)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# create a local_resource from each file
|
|
22
|
+
# making sure to skip any that do not match the include/exclude patterns
|
|
23
|
+
manifest_hash.lazily.each { |key, value|
|
|
24
|
+
path = source_dir + key
|
|
25
|
+
next if !path.fnmatch?(include.to_s) or path.fnmatch?(exclude.to_s || '')
|
|
26
|
+
file = path_to_local_resource(path, mtime: value['mtime'], digest: value['digest'], content_length: value['size'])
|
|
27
|
+
files << file
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
files
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def locate_manifest(dir)
|
|
36
|
+
Dir.glob(dir.to_s + 'manifest*.{json,yaml,yml}').max { |f| File.ctime(f) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def parse_manifest(manifest_path)
|
|
40
|
+
manifest_hash = {}
|
|
41
|
+
manifest_data = File.read(manifest_path)
|
|
42
|
+
|
|
43
|
+
# manifest files can be YAML or JSON but Sprockets::Manifest isn't backwards compatible with that in mind :(
|
|
44
|
+
case File.extname(manifest_path)
|
|
45
|
+
when '.json'
|
|
46
|
+
manifest_hash = MultiJson.load(manifest_data)
|
|
47
|
+
when '.yml', '.yaml'
|
|
48
|
+
manifest_hash = YAML.load(manifest_data)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# different versions of Sprockets have different manifest layouts, lets try and work around this by checking for the presence of "files" and "assets" in the manifest first
|
|
52
|
+
# else we know it must be an old manifest file, so its root is "files"
|
|
53
|
+
if manifest_hash.key?('files') || manifest_hash.key?('assets')
|
|
54
|
+
manifest_hash = manifest_hash['files']
|
|
55
|
+
else
|
|
56
|
+
|
|
57
|
+
# index.* files are special and should be ignored from sync
|
|
58
|
+
# something which seems to only happen in older versions of Sprockets
|
|
59
|
+
manifest_hash.delete_if { |key, value|
|
|
60
|
+
key.include?('/index.')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# lets manipulate this older manifest to appear similiar to the newer manifest's "files"
|
|
64
|
+
modified_manifest_hash = {}
|
|
65
|
+
manifest_hash.each { |key, value|
|
|
66
|
+
modified_manifest_hash[value] = {
|
|
67
|
+
'logical_path' => key,
|
|
68
|
+
'mtime' => nil,
|
|
69
|
+
'size' => nil,
|
|
70
|
+
'digest' => nil
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#
|
|
75
|
+
manifest_hash = modified_manifest_hash
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
manifest_hash
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'fog'
|
|
2
|
+
require 'lazily'
|
|
3
|
+
require 'virtus'
|
|
4
|
+
require 'pathname'
|
|
5
|
+
require 'celluloid'
|
|
6
|
+
require 'multi_sync/mixins/log_helper'
|
|
7
|
+
|
|
8
|
+
module MultiSync
|
|
9
|
+
class Target
|
|
10
|
+
include Virtus
|
|
11
|
+
include Celluloid
|
|
12
|
+
include MultiSync::Mixins::LogHelper
|
|
13
|
+
|
|
14
|
+
attribute :connection
|
|
15
|
+
attribute :target_dir, Pathname
|
|
16
|
+
attribute :destination_dir, Pathname
|
|
17
|
+
attribute :credentials, Hash, default: :default_credentials
|
|
18
|
+
|
|
19
|
+
# Initialize a new Target object
|
|
20
|
+
#
|
|
21
|
+
# @param options [Hash]
|
|
22
|
+
def initialize(options = {})
|
|
23
|
+
fail(ArgumentError, 'target_dir must be present') unless options[:target_dir]
|
|
24
|
+
self.target_dir = Pathname.new(options.fetch(:target_dir, ''))
|
|
25
|
+
self.destination_dir = Pathname.new(options.fetch(:destination_dir, ''))
|
|
26
|
+
credentials.merge!(options.fetch(:credentials, {}))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def default_credentials
|
|
30
|
+
# deep clone just in case
|
|
31
|
+
Marshal.load(Marshal.dump(MultiSync.credentials))
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|