middleman-s3_sync 3.0.47 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7b9818aa4b2d7384ea359655f3ac8a3b051fafbd
4
- data.tar.gz: 8416884e3a0bcd584f82f144e3a0d480780ae6af
3
+ metadata.gz: 0ea0d9098ccfca7a420089e791da7f7881298c5c
4
+ data.tar.gz: 93b501fb586db5be0cd329c448ac2edb5da3c044
5
5
  SHA512:
6
- metadata.gz: 63ee52ab134fc7aff450acb5dcf5d865639ea163b948c5a8b430000f09216fc523eb9267f774379f85e4b4e27c62a5892a068737bf9b081b62a3a73f44eb30d4
7
- data.tar.gz: d65744542b6d42817502f101b174cee9bc5893cbb5061a6a41b4b161ca1ad0d2859f80f60c6b78585344e4dd85805b8abe2467d4e14016d7662b500fe9837505
6
+ metadata.gz: 1d4b328645fe0171b6b5f01c0b0f2f19428c17a06b0e0af8bfa9b908763982e5a12b52b2c33e7b1a57b4e7e6bdd424ae27e302d97b17db7a8f08654868239615
7
+ data.tar.gz: 9d427de8aca80db2ff476dc42da6a2fc11bb769651c8f16a24780dbc0b9a9ae51459a038ae8c85abff8a45c5728d511791254bdc8566368f8d066a1d0089453a
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  language: ruby
3
+ sudo: false
4
+ cache: bundler
3
5
  rvm:
4
- - 1.9.3
5
6
  - 2.0.0
6
7
  - 2.1.5
7
- - 2.2.1
8
+ - 2.2.3
8
9
 
9
10
  notifications:
10
11
  webhooks:
@@ -1,7 +1,8 @@
1
1
  require 'middleman-core'
2
+ require 'middleman-s3_sync/commands'
2
3
  require 'middleman/s3_sync'
3
4
 
4
- ::Middleman::Extensions.register(:s3_sync, '>= 3.0.0') do
5
- ::Middleman::S3Sync
5
+ ::Middleman::Extensions.register(:s3_sync) do
6
+ require 'middleman-s3_sync/extension'
7
+ ::Middleman::S3SyncExtension
6
8
  end
7
-
@@ -2,6 +2,8 @@ require 'middleman-core/cli'
2
2
 
3
3
  module Middleman
4
4
  module Cli
5
+ Base.map("sync" => "s3_sync")
6
+
5
7
  class S3Sync < Thor
6
8
  include Thor::Actions
7
9
 
@@ -13,11 +15,11 @@ module Middleman
13
15
  true
14
16
  end
15
17
 
16
- desc "s3_sync", "Pushes the minimum set of files needed to S3"
17
- method_option :force, type: :boolean,
18
+ desc "s3_sync [options]", "Synchronizes a middleman site to an AWS S3 bucket"
19
+ class_option :force, type: :boolean,
18
20
  desc: "Push all local files to the server",
19
21
  aliases: '-f'
20
- method_option :bucket, type: :string,
22
+ class_option :bucket, type: :string,
21
23
  desc: "Specify which bucket to use, overrides the configured bucket.",
22
24
  aliases: '-b'
23
25
  method_option :prefix, type: :string,
@@ -26,25 +28,31 @@ module Middleman
26
28
  method_option :verbose, type: :boolean,
27
29
  desc: "Adds more verbosity...",
28
30
  aliases: '-v'
31
+ class_option :dry_run, type: :boolean,
32
+ desc: "Performs a dry run of the sync",
33
+ aliases: '-n'
29
34
 
30
35
  def s3_sync
31
- shared_inst = ::Middleman::Application.server.inst
32
- bucket = shared_inst.s3_sync_options.bucket rescue nil
36
+ ::Middleman::S3Sync.app = ::Middleman::Application.server.inst
37
+
38
+ s3_sync_options = ::Middleman::S3Sync.s3_sync_options
39
+
40
+ bucket = s3_sync_options.bucket rescue nil
41
+
33
42
  unless bucket
34
43
  raise Thor::Error.new "You need to activate the s3_sync extension and at least provide the bucket name."
35
44
  end
36
45
 
37
- s3_sync_options = shared_inst.s3_sync_options
38
46
 
39
47
  # Override options based on what was passed on the command line...
40
48
  s3_sync_options.force = options[:force] if options[:force]
41
49
  s3_sync_options.bucket = options[:bucket] if options[:bucket]
42
50
  s3_sync_options.verbose = options[:verbose] if options[:verbose]
43
51
  s3_sync_options.prefix = options[:prefix] if options[:prefix]
52
+ s3_sync_options.dry_run = options[:dry_run] if options[:dry_run]
44
53
 
45
- ::Middleman::S3Sync.sync(s3_sync_options)
54
+ ::Middleman::S3Sync.sync()
46
55
  end
47
56
  end
48
- Base.map('sync' => 's3_sync')
49
57
  end
50
58
  end
@@ -1,59 +1,96 @@
1
1
  require 'middleman-core'
2
+ require 'middleman/s3_sync'
2
3
  require 'map'
3
4
 
4
5
  module Middleman
5
- module S3Sync
6
- class << self
7
- attr_accessor :options
6
+ class S3SyncExtension < Extension
7
+ # Options supported by the extension...
8
+ option :prefix, nil, 'Path prefix of the resource we are looking for on the server.'
9
+ option :http_prefix, nil, 'Path prefix of the resources'
10
+ option :acl, 'public-read', 'ACL for the resources being pushed to S3'
11
+ option :bucket, 'nil', 'The name of the bucket we are pushing to.'
12
+ option :region, 'us-east-1', 'The name of the AWS region hosting the S3 bucket'
13
+ option :aws_access_key_id, ENV['AWS_ACCESS_KEY_ID'] , 'The AWS access key id'
14
+ option :aws_secret_access_key, ENV['AWS_SECRET_ACCESS_KEY'], 'The AWS secret access key'
15
+ option :after_build, false, 'Whether to synchronize right after the build'
16
+ option :build_dir, nil, 'Where the built site is stored'
17
+ option :delete, true, 'Whether to delete resources that do not have a local equivalent'
18
+ option :encryption, false, 'Whether to encrypt the content on the S3 bucket'
19
+ option :force, false, 'Whether to push all current resources to S3'
20
+ option :prefer_gzip, true, 'Whether to push the compressed version of the resource to S3'
21
+ option :reduced_redundancy_storage, nil, 'Whether to use the reduced redundancy storage option'
22
+ option :path_style, true, 'Whether to use path_style URLs to communiated with S3'
23
+ option :version_bucket, false, 'Whether to enable versionning on the S3 bucket content'
24
+ option :verbose, false, 'Whether to provide more verbose output'
25
+ option :dry_run, false, 'Whether to perform a dry-run'
8
26
 
9
- def registered(app, options_hash = {}, &block)
10
- options = Options.new
11
- yield options if block_given?
27
+ def initialize(app, options_hash = {}, &block)
28
+ super
29
+ app.define_hook :after_s3_sync
30
+ # Temporary workaround for 3.3 and 3.4.
31
+ app.send :include, ClassMethods
32
+ end
33
+
34
+ def after_configuration
35
+ read_config
36
+ options.aws_access_key_id ||= ENV['AWS_ACCESS_KEY_ID']
37
+ options.aws_secret_access_key ||= ENV['AWS_SECRET_ACCESS_KEY']
38
+ options.http_prefix = app.http_prefix if app.respond_to? :http_prefix
39
+ options.build_dir ||= app.build_dir if app.respond_to? :build_dir
40
+ ::Middleman::S3Sync.s3_sync_options = s3_sync_options
41
+ end
12
42
 
13
- @options = options
43
+ def after_build
44
+ ::Middleman::S3Sync.sync() if options.after_build
45
+ end
14
46
 
15
- app.send :include, Helpers
47
+ def manipulate_resource_list(mm_resources)
48
+ ::Middleman::S3Sync.mm_resources = mm_resources
49
+ end
16
50
 
17
- app.define_hook :after_s3_sync
51
+ def s3_sync_options
52
+ options
53
+ end
18
54
 
19
- app.after_configuration do |config|
20
- # Record the http_prefix if it is set. We will need it while setting
21
- # the options to detect whether it is set and adjust the prefix value
22
- # accordingly
23
- options.http_prefix = app.respond_to? :http_prefix ? app.http_prefix : nil
55
+ # Read config options from an IO stream and set them on `self`. Defaults
56
+ # to reading from the `.s3_sync` file in the MM project root if it exists.
57
+ #
58
+ # @param io [IO] an IO stream to read from
59
+ # @return [void]
60
+ def read_config(io = nil)
61
+ unless io
62
+ root_path = ::Middleman::Application.root
63
+ config_file_path = File.join(root_path, ".s3_sync")
24
64
 
25
- # Define the after_build step after during configuration so
26
- # that it's pushed to the end of the callback chain
27
- app.after_build do |builder|
28
- ::Middleman::S3Sync.sync(options) if options.after_build
29
- end
65
+ # skip if config file does not exist
66
+ return unless File.exists?(config_file_path)
30
67
 
31
- options.build_dir ||= build_dir
32
- end
68
+ io = File.open(config_file_path, "r")
33
69
  end
34
- alias :included :registered
35
70
 
36
- def s3_sync_options
37
- @options
38
- end
71
+ config = YAML.load(io).symbolize_keys
39
72
 
40
- def s3_sync_options=(options)
41
- @options = options
73
+ OPTIONS.each do |config_option|
74
+ self.send("#{config_option}=".to_sym, config[config_option]) if config[config_option]
42
75
  end
76
+ end
43
77
 
44
- module Helpers
45
- def s3_sync_options
46
- ::Middleman::S3Sync.s3_sync_options
47
- end
48
78
 
49
- def default_caching_policy(policy = {})
50
- s3_sync_options.add_caching_policy(:default, policy)
51
- end
79
+ module ClassMethods
80
+ def s3_sync_options
81
+ ::Middleman::S3SyncExtension.s3_sync_options
82
+ end
52
83
 
53
- def caching_policy(content_type, policy = {})
54
- s3_sync_options.add_caching_policy(content_type, policy)
55
- end
84
+ def default_caching_policy(policy = {})
85
+ ::Middleman::S3Sync.add_caching_policy(:default, policy)
86
+ end
87
+
88
+ def caching_policy(content_type, policy = {})
89
+ ::Middleman::S3Sync.add_caching_policy(content_type, policy)
56
90
  end
57
91
  end
58
92
  end
93
+
94
+ ::Middleman::Extensions.register(:s3_sync, S3SyncExtension)
59
95
  end
96
+
@@ -1,12 +1,13 @@
1
1
  require 'fog/aws'
2
- require 'pmap'
2
+ require 'fog/aws/storage'
3
3
  require 'digest/md5'
4
4
  require 'middleman/s3_sync/version'
5
5
  require 'middleman/s3_sync/options'
6
- require 'middleman-s3_sync/commands'
6
+ require 'middleman/s3_sync/caching_policy'
7
7
  require 'middleman/s3_sync/status'
8
8
  require 'middleman/s3_sync/resource'
9
9
  require 'middleman-s3_sync/extension'
10
+ require 'parallel'
10
11
  require 'ruby-progressbar'
11
12
  require 'thread'
12
13
 
@@ -14,20 +15,23 @@ module Middleman
14
15
  module S3Sync
15
16
  class << self
16
17
  include Status
18
+ include CachingPolicy
19
+
17
20
  @@bucket_lock = Mutex.new
18
21
  @@bucket_files_lock = Mutex.new
19
22
 
20
- def sync(options)
21
- @app = ::Middleman::Application.server.inst
22
-
23
- self.s3_sync_options = options
23
+ attr_accessor :s3_sync_options
24
+ attr_accessor :mm_resources
25
+ attr_reader :app
24
26
 
27
+ def sync()
28
+ say_status "Let's see if there's work to be done..."
25
29
  unless work_to_be_done?
26
- say_status "\nAll S3 files are up to date."
30
+ say_status "All S3 files are up to date."
27
31
  return
28
32
  end
29
33
 
30
- say_status "\nReady to apply updates to #{s3_sync_options.bucket}."
34
+ say_status "Ready to apply updates to #{s3_sync_options.bucket}."
31
35
 
32
36
  update_bucket_versioning
33
37
 
@@ -36,7 +40,7 @@ module Middleman
36
40
  update_resources
37
41
  delete_resources
38
42
 
39
- @app.run_hook :after_s3_sync, ignored: files_to_ignore.map(&:path),
43
+ app.run_hook :after_s3_sync, ignored: files_to_ignore.map(&:path),
40
44
  created: files_to_create.map(&:path),
41
45
  updated: files_to_update.map(&:path),
42
46
  deleted: files_to_delete.map(&:path)
@@ -52,13 +56,29 @@ module Middleman
52
56
  end
53
57
  end
54
58
 
59
+ def add_local_resource(mm_resource)
60
+ s3_sync_resources[mm_resource.destination_path] = S3Sync::Resource.new(mm_resource, remote_resource_for_path(mm_resource.destination_path)).tap(&:status)
61
+ end
62
+
63
+ def remote_only_paths
64
+ paths - s3_sync_resources.keys
65
+ end
66
+
67
+ def app=(app)
68
+ @app = app
69
+ @app.extend ::Middleman::S3SyncExtension::ClassMethods
70
+ end
71
+
72
+ def content_types
73
+ @content_types || {}
74
+ end
75
+
55
76
  protected
56
77
  def update_bucket_versioning
57
78
  connection.put_bucket_versioning(s3_sync_options.bucket, "Enabled") if s3_sync_options.version_bucket
58
79
  end
59
80
 
60
81
  def connection
61
-
62
82
  connection_options = {
63
83
  :region => s3_sync_options.region,
64
84
  :path_style => s3_sync_options.path_style
@@ -76,37 +96,20 @@ module Middleman
76
96
  @connection ||= Fog::Storage::AWS.new(connection_options)
77
97
  end
78
98
 
79
- def resources
80
- @resources ||= paths.pmap(32) do |p|
81
- progress_bar.increment
82
- S3Sync::Resource.new(p, bucket_files.find { |f| f.key == "#{s3_sync_options.prefix}#{p}" }).tap(&:status)
83
- end
99
+ def remote_resource_for_path(path)
100
+ bucket_files.find { |f| f.key == "#{s3_sync_options.prefix}#{path}" }
84
101
  end
85
102
 
86
- def progress_bar
87
- @progress_bar ||= ProgressBar.create(total: paths.length)
103
+ def s3_sync_resources
104
+ @s3_sync_resources ||= {}
88
105
  end
89
106
 
90
107
  def paths
91
108
  @paths ||= begin
92
- say_status "Gathering the paths to evaluate."
93
- (remote_paths.map { |rp| rp.gsub(/^#{s3_sync_options.prefix}/, '')} + local_paths).uniq.sort
109
+ (remote_paths.map { |rp| rp.gsub(/^#{s3_sync_options.prefix}/, '')} + s3_sync_resources.keys).uniq.sort
94
110
  end
95
111
  end
96
112
 
97
- def local_paths
98
- @local_paths ||= begin
99
- local_paths = (Dir[build_dir + "/**/*"] + Dir[build_dir + "/**/.*"])
100
- .reject { |p| File.directory?(p) }
101
-
102
- if s3_sync_options.prefer_gzip
103
- local_paths.reject! { |p| p =~ /\.gz$/ && File.exist?(p.gsub(/\.gz$/, '')) }
104
- end
105
-
106
- local_paths.pmap(32) { |p| p.gsub(/#{build_dir}\//, '') }
107
- end
108
- end
109
-
110
113
  def remote_paths
111
114
  @remote_paths ||= if s3_sync_options.delete
112
115
  bucket_files.map(&:key)
@@ -150,27 +153,33 @@ module Middleman
150
153
  end
151
154
 
152
155
  def work_to_be_done?
156
+ Parallel.each(mm_resources, in_threads: 8, progress: "Processing sitemap") { |mm_resource| add_local_resource(mm_resource) }
157
+
158
+ Parallel.each(remote_only_paths, in_threads: 8, progress: "Processing remote files") do |remote_path|
159
+ s3_sync_resources[remote_path] ||= S3Sync::Resource.new(nil, remote_resource_for_path(remote_path)).tap(&:status)
160
+ end
161
+
153
162
  !(files_to_create.empty? && files_to_update.empty? && files_to_delete.empty?)
154
163
  end
155
164
 
156
165
  def files_to_delete
157
- @files_to_delete ||= if s3_sync_options.delete
158
- resources.select { |r| r.to_delete? }
159
- else
160
- []
161
- end
166
+ if s3_sync_options.delete
167
+ s3_sync_resources.values.select { |r| r.to_delete? }
168
+ else
169
+ []
170
+ end
162
171
  end
163
172
 
164
173
  def files_to_create
165
- @files_to_create ||= resources.select { |r| r.to_create? }
174
+ s3_sync_resources.values.select { |r| r.to_create? }
166
175
  end
167
176
 
168
177
  def files_to_update
169
- @files_to_update ||= resources.select { |r| r.to_update? }
178
+ s3_sync_resources.values.select { |r| r.to_update? }
170
179
  end
171
180
 
172
181
  def files_to_ignore
173
- @files_to_ignore ||= resources.select { |r| r.to_ignore? }
182
+ s3_sync_resources.values.select { |r| r.to_ignore? }
174
183
  end
175
184
 
176
185
  def build_dir
@@ -0,0 +1,58 @@
1
+ require 'map'
2
+
3
+ module Middleman
4
+ module S3Sync
5
+ module CachingPolicy
6
+ def add_caching_policy(content_type, options)
7
+ caching_policies[content_type.to_s] = BrowserCachePolicy.new(options)
8
+ end
9
+
10
+ def caching_policy_for(content_type)
11
+ caching_policies.fetch(content_type.to_s, caching_policies[:default])
12
+ end
13
+
14
+ def default_caching_policy
15
+ caching_policies[:default]
16
+ end
17
+
18
+ def caching_policies
19
+ @caching_policies ||= Map.new
20
+ end
21
+ end
22
+
23
+ class BrowserCachePolicy
24
+ attr_accessor :policies
25
+
26
+ def initialize(options = {})
27
+ @policies = Map.from_hash(options)
28
+ end
29
+
30
+ def cache_control
31
+ policy = []
32
+ policy << "max-age=#{policies.max_age}" if policies.has_key?(:max_age)
33
+ policy << "s-maxage=#{policies.s_maxage}" if policies.has_key?(:s_maxage)
34
+ policy << "public" if policies.fetch(:public, false)
35
+ policy << "private" if policies.fetch(:private, false)
36
+ policy << "no-cache" if policies.fetch(:no_cache, false)
37
+ policy << "no-store" if policies.fetch(:no_store, false)
38
+ policy << "must-revalidate" if policies.fetch(:must_revalidate, false)
39
+ policy << "proxy-revalidate" if policies.fetch(:proxy_revalidate, false)
40
+ if policy.empty?
41
+ nil
42
+ else
43
+ policy.join(", ")
44
+ end
45
+ end
46
+
47
+ def to_s
48
+ cache_control
49
+ end
50
+
51
+ def expires
52
+ if expiration = policies.fetch(:expires, nil)
53
+ CGI.rfc1123_date(expiration)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -19,36 +19,16 @@ module Middleman
19
19
  :reduced_redundancy_storage,
20
20
  :path_style,
21
21
  :version_bucket,
22
+ :dry_run,
22
23
  :verbose,
23
24
  :content_types
24
25
  ]
25
26
  attr_accessor *OPTIONS
26
27
 
27
- def initialize
28
- # read config from .s3_sync on initialization
29
- self.read_config
30
- end
31
-
32
28
  def acl
33
29
  @acl || 'public-read'
34
30
  end
35
31
 
36
- def add_caching_policy(content_type, options)
37
- caching_policies[content_type.to_s] = BrowserCachePolicy.new(options)
38
- end
39
-
40
- def caching_policy_for(content_type)
41
- caching_policies.fetch(content_type.to_s, caching_policies[:default])
42
- end
43
-
44
- def default_caching_policy
45
- caching_policies[:default]
46
- end
47
-
48
- def caching_policies
49
- @caching_policies ||= Map.new
50
- end
51
-
52
32
  def aws_access_key_id=(aws_access_key_id)
53
33
  @aws_access_key_id = aws_access_key_id if aws_access_key_id
54
34
  end
@@ -95,75 +75,13 @@ module Middleman
95
75
  end
96
76
 
97
77
  def prefix
98
- @prefix.blank? ? "" : "#{@prefix}/"
78
+ @prefix.nil? || @prefix.empty? ? "" : "#{@prefix}/"
99
79
  end
100
80
 
101
81
  def version_bucket
102
82
  @version_bucket.nil? ? false : @version_bucket
103
83
  end
104
84
 
105
- def content_types
106
- @content_types || {}
107
- end
108
-
109
- # Read config options from an IO stream and set them on `self`. Defaults
110
- # to reading from the `.s3_sync` file in the MM project root if it exists.
111
- #
112
- # @param io [IO] an IO stream to read from
113
- # @return [void]
114
- def read_config(io = nil)
115
- unless io
116
- root_path = ::Middleman::Application.root
117
- config_file_path = File.join(root_path, ".s3_sync")
118
-
119
- # skip if config file does not exist
120
- return unless File.exists?(config_file_path)
121
-
122
- io = File.open(config_file_path, "r")
123
- end
124
-
125
- config = YAML.load(io).symbolize_keys
126
-
127
- OPTIONS.each do |config_option|
128
- self.send("#{config_option}=".to_sym, config[config_option]) if config[config_option]
129
- end
130
- end
131
-
132
- protected
133
- class BrowserCachePolicy
134
- attr_accessor :policies
135
-
136
- def initialize(options)
137
- @policies = Map.from_hash(options)
138
- end
139
-
140
- def cache_control
141
- policy = []
142
- policy << "max-age=#{policies.max_age}" if policies.has_key?(:max_age)
143
- policy << "s-maxage=#{policies.s_maxage}" if policies.has_key?(:s_maxage)
144
- policy << "public" if policies.fetch(:public, false)
145
- policy << "private" if policies.fetch(:private, false)
146
- policy << "no-cache" if policies.fetch(:no_cache, false)
147
- policy << "no-store" if policies.fetch(:no_store, false)
148
- policy << "must-revalidate" if policies.fetch(:must_revalidate, false)
149
- policy << "proxy-revalidate" if policies.fetch(:proxy_revalidate, false)
150
- if policy.empty?
151
- nil
152
- else
153
- policy.join(", ")
154
- end
155
- end
156
-
157
- def to_s
158
- cache_control
159
- end
160
-
161
- def expires
162
- if expiration = policies.fetch(:expires, nil)
163
- CGI.rfc1123_date(expiration)
164
- end
165
- end
166
- end
167
85
  end
168
86
  end
169
87
  end
@@ -1,14 +1,15 @@
1
1
  module Middleman
2
2
  module S3Sync
3
3
  class Resource
4
- attr_accessor :path, :partial_s3_resource, :full_s3_resource, :content_type, :gzipped, :options
4
+ attr_accessor :path, :resource, :partial_s3_resource, :full_s3_resource, :content_type, :gzipped, :options
5
5
 
6
6
  CONTENT_MD5_KEY = 'x-amz-meta-content-md5'
7
7
 
8
8
  include Status
9
9
 
10
- def initialize(path, partial_s3_resource)
11
- @path = path
10
+ def initialize(resource, partial_s3_resource)
11
+ @resource = resource
12
+ @path = resource ? resource.destination_path : partial_s3_resource.key
12
13
  @partial_s3_resource = partial_s3_resource
13
14
  end
14
15
 
@@ -61,7 +62,7 @@ module Middleman
61
62
  s3_resource.merge_attributes(to_h)
62
63
  s3_resource.body = body
63
64
 
64
- s3_resource.save
65
+ s3_resource.save unless options.dry_run
65
66
  }
66
67
  end
67
68
 
@@ -76,23 +77,23 @@ module Middleman
76
77
 
77
78
  def destroy!
78
79
  say_status ANSI.red { "Deleting" } + " " + remote_path
79
- bucket.files.destroy remote_path
80
+ bucket.files.destroy remote_path unless options.dry_run
80
81
  end
81
82
 
82
83
  def create!
83
84
  say_status ANSI.green { "Creating" } + " #{remote_path}#{ gzipped ? ANSI.white {' (gzipped)'} : ''}"
84
85
  local_content { |body|
85
- bucket.files.create(to_h.merge(body: body))
86
+ bucket.files.create(to_h.merge(body: body)) unless options.dry_run
86
87
  }
87
88
  end
88
89
 
89
90
  def ignore!
90
- reason = if redirect?
91
- :redirect
92
- elsif directory?
93
- :directory
94
- end
95
91
  if options.verbose
92
+ reason = if redirect?
93
+ :redirect
94
+ elsif directory?
95
+ :directory
96
+ end
96
97
  say_status ANSI.yellow {"Ignoring"} + " #{remote_path} #{ reason ? ANSI.white {"(#{reason})" } : "" }"
97
98
  end
98
99
  end
@@ -135,6 +136,8 @@ module Middleman
135
136
  elsif local? && remote?
136
137
  if options.force
137
138
  :updated
139
+ elsif not caching_policy_match?
140
+ :updated
138
141
  elsif local_object_md5 == remote_object_md5
139
142
  :identical
140
143
  else
@@ -155,8 +158,10 @@ module Middleman
155
158
  :new
156
159
  elsif remote? && redirect?
157
160
  :ignored
158
- else
161
+ elsif remote?
159
162
  :deleted
163
+ else
164
+ :ignored
160
165
  end
161
166
  end
162
167
 
@@ -205,12 +210,20 @@ module Middleman
205
210
  end
206
211
 
207
212
  def content_type
208
- @content_type ||= options.content_types[path]
209
- @content_type ||= MIME::Types.of(path).first
213
+ @content_type ||= Middleman::S3Sync.content_types[path]
214
+ @content_type ||= resource.content_type
210
215
  end
211
216
 
212
217
  def caching_policy
213
- @caching_policy ||= options.caching_policy_for(content_type)
218
+ @caching_policy ||= Middleman::S3Sync.caching_policy_for(content_type)
219
+ end
220
+
221
+ def caching_policy_match?
222
+ if (caching_policy)
223
+ caching_policy.cache_control == full_s3_resource.cache_control
224
+ else
225
+ true
226
+ end
214
227
  end
215
228
 
216
229
  protected
@@ -1,5 +1,5 @@
1
1
  module Middleman
2
2
  module S3Sync
3
- VERSION = "3.0.47"
3
+ VERSION = "3.3.0"
4
4
  end
5
5
  end
@@ -18,19 +18,20 @@ Gem::Specification.new do |gem|
18
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
19
  gem.require_paths = ["lib"]
20
20
 
21
- gem.add_runtime_dependency 'middleman-core', '>= 3.0.0'
21
+ gem.add_runtime_dependency 'middleman-core', '~> 3.3'
22
22
  gem.add_runtime_dependency 'unf'
23
23
  gem.add_runtime_dependency 'fog-aws', '>= 0.1.1'
24
24
  gem.add_runtime_dependency 'map'
25
- gem.add_runtime_dependency 'pmap'
25
+ gem.add_runtime_dependency 'parallel'
26
26
  gem.add_runtime_dependency 'ruby-progressbar'
27
27
  gem.add_runtime_dependency 'ansi', '~> 1.5.0'
28
28
 
29
29
  gem.add_development_dependency 'rake'
30
30
  gem.add_development_dependency 'pry'
31
- gem.add_development_dependency 'pry-nav'
31
+ gem.add_development_dependency 'pry-byebug'
32
32
  gem.add_development_dependency 'rspec', '>= 3.0.0'
33
33
  gem.add_development_dependency 'rspec-its'
34
+ gem.add_development_dependency 'rspec-mocks'
34
35
  gem.add_development_dependency 'timerizer'
35
36
  gem.add_development_dependency 'travis'
36
37
  gem.add_development_dependency 'travis-lint'
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+ require 'middleman/s3_sync/caching_policy'
3
+
4
+ describe Middleman::S3Sync::BrowserCachePolicy do
5
+ let(:options) { Hash.new }
6
+ subject(:policy) { Middleman::S3Sync::BrowserCachePolicy.new(options) }
7
+
8
+ it "should be blank" do
9
+ policy.should_not be_nil
10
+
11
+ policy.to_s.should_not =~ /max-age=/
12
+ policy.to_s.should_not =~ /s-maxage=/
13
+ policy.to_s.should_not =~ /public/
14
+ policy.to_s.should_not =~ /private/
15
+ policy.to_s.should_not =~ /no-cache/
16
+ policy.to_s.should_not =~ /no-store/
17
+ policy.to_s.should_not =~ /must-revalidate/
18
+ policy.to_s.should_not =~ /proxy-revalidate/
19
+ policy.expires.should be_nil
20
+ end
21
+
22
+ context "setting max-age" do
23
+ let(:options) { { max_age: 300 } }
24
+
25
+ its(:to_s) { should =~ /max-age=300/ }
26
+ end
27
+
28
+ context "setting s-maxage" do
29
+ let(:options) { { s_maxage: 300 } }
30
+
31
+ its(:to_s) { should =~ /s-maxage=300/ }
32
+ end
33
+
34
+ context "set public flag" do
35
+ let(:options) { { public: true } }
36
+ its(:to_s) { should =~ /public/ }
37
+ end
38
+
39
+ context "it should set the private flag if it is set to true" do
40
+ let(:options) { { private: true } }
41
+ its(:to_s) { should =~ /private/ }
42
+ end
43
+
44
+ context "it should set the no-cache flag when set property" do
45
+ let(:options) { { no_cache: true }}
46
+ its(:to_s) { should =~ /no-cache/ }
47
+ end
48
+
49
+ context "setting the no-store flag" do
50
+ let(:options) { { no_store: true } }
51
+ its(:to_s) { should =~ /no-store/ }
52
+ end
53
+
54
+ context "setting the must-revalidate policy" do
55
+ let(:options) { { must_revalidate: true } }
56
+ its(:to_s) { should =~ /must-revalidate/ }
57
+ end
58
+
59
+ context "setting the proxy-revalidate policy" do
60
+ let(:options) { { proxy_revalidate: true } }
61
+ its(:to_s) { should =~ /proxy-revalidate/ }
62
+ end
63
+
64
+ context "divide caching policiies with a comma and a space" do
65
+ let(:options) { { :max_age => 300, :public => true } }
66
+
67
+ it "splits policies eith commans and spaces" do
68
+ policies = policy.to_s.split(/, /)
69
+ policies.length.should == 2
70
+ policies.first.should == 'max-age=300'
71
+ policies.last.should == 'public'
72
+ end
73
+ end
74
+
75
+ context "set the expiration date" do
76
+ let(:options) { { expires: 1.years.from_now } }
77
+
78
+ its(:expires) { should == CGI.rfc1123_date(1.year.from_now )}
79
+ end
80
+ end
@@ -6,14 +6,19 @@ describe Middleman::S3Sync::Resource do
6
6
  Middleman::S3Sync::Options.new
7
7
  }
8
8
 
9
+ let(:mm_resource) {
10
+ double(
11
+ destination_path: 'path/to/resource.html'
12
+ )
13
+ }
9
14
  before do
10
- Middleman::S3Sync.options = options
15
+ Middleman::S3Sync.s3_sync_options = options
11
16
  options.build_dir = "build"
12
17
  options.prefer_gzip = false
13
18
  end
14
19
 
15
20
  context "a new resource" do
16
- subject(:resource) { Middleman::S3Sync::Resource.new('path/to/resource.html', nil) }
21
+ subject(:resource) { Middleman::S3Sync::Resource.new(mm_resource, nil) }
17
22
 
18
23
  context "without a prefix" do
19
24
  before do
@@ -75,14 +80,17 @@ describe Middleman::S3Sync::Resource do
75
80
  end
76
81
 
77
82
  context "the file does not exist locally" do
78
- subject(:resource) { Middleman::S3Sync::Resource.new('path/to/resource.html', remote) }
83
+ subject(:resource) { Middleman::S3Sync::Resource.new(nil, remote) }
79
84
 
80
- let(:remote) { double("remote") }
85
+ let(:remote) {
86
+ double(
87
+ key: 'path/to/resource.html',
88
+ metadata: {}
89
+ )
90
+ }
81
91
 
82
92
  before do
83
- allow(remote).to receive(:key).and_return('path/to/resource.html')
84
- allow(remote).to receive(:metadata).and_return({})
85
- resource.full_s3_resource = remote
93
+ resource.full_s3_resource = remote
86
94
  end
87
95
 
88
96
  context "without a prefix" do
@@ -116,7 +124,7 @@ describe Middleman::S3Sync::Resource do
116
124
  expect(resource).to be_remote
117
125
  end
118
126
 
119
- it "exists locally" do
127
+ it "does not exist locally" do
120
128
  expect(resource).not_to be_local
121
129
  end
122
130
 
@@ -10,7 +10,6 @@ require 'timerizer'
10
10
  require 'rspec/its'
11
11
 
12
12
  RSpec.configure do |config|
13
- config.treat_symbols_as_metadata_keys_with_true_values = true
14
13
  config.run_all_when_everything_filtered = true
15
14
  config.filter_run :focus
16
15
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: middleman-s3_sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.47
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Frederic Jean
@@ -9,22 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-06-15 00:00:00.000000000 Z
12
+ date: 2015-09-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: middleman-core
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - ">="
18
+ - - "~>"
19
19
  - !ruby/object:Gem::Version
20
- version: 3.0.0
20
+ version: '3.3'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - ">="
25
+ - - "~>"
26
26
  - !ruby/object:Gem::Version
27
- version: 3.0.0
27
+ version: '3.3'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: unf
30
30
  requirement: !ruby/object:Gem::Requirement
@@ -68,7 +68,7 @@ dependencies:
68
68
  - !ruby/object:Gem::Version
69
69
  version: '0'
70
70
  - !ruby/object:Gem::Dependency
71
- name: pmap
71
+ name: parallel
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
74
  - - ">="
@@ -138,7 +138,7 @@ dependencies:
138
138
  - !ruby/object:Gem::Version
139
139
  version: '0'
140
140
  - !ruby/object:Gem::Dependency
141
- name: pry-nav
141
+ name: pry-byebug
142
142
  requirement: !ruby/object:Gem::Requirement
143
143
  requirements:
144
144
  - - ">="
@@ -179,6 +179,20 @@ dependencies:
179
179
  - - ">="
180
180
  - !ruby/object:Gem::Version
181
181
  version: '0'
182
+ - !ruby/object:Gem::Dependency
183
+ name: rspec-mocks
184
+ requirement: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ type: :development
190
+ prerelease: false
191
+ version_requirements: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - ">="
194
+ - !ruby/object:Gem::Version
195
+ version: '0'
182
196
  - !ruby/object:Gem::Dependency
183
197
  name: timerizer
184
198
  requirement: !ruby/object:Gem::Requirement
@@ -241,13 +255,14 @@ files:
241
255
  - lib/middleman-s3_sync/commands.rb
242
256
  - lib/middleman-s3_sync/extension.rb
243
257
  - lib/middleman/s3_sync.rb
258
+ - lib/middleman/s3_sync/caching_policy.rb
244
259
  - lib/middleman/s3_sync/options.rb
245
260
  - lib/middleman/s3_sync/resource.rb
246
261
  - lib/middleman/s3_sync/status.rb
247
262
  - lib/middleman/s3_sync/version.rb
248
263
  - lib/middleman_extension.rb
249
264
  - middleman-s3_sync.gemspec
250
- - spec/options_spec.rb
265
+ - spec/caching_policy_spec.rb
251
266
  - spec/resource_spec.rb
252
267
  - spec/spec_helper.rb
253
268
  homepage: http://github.com/fredjean/middleman-s3_sync
@@ -275,6 +290,6 @@ signing_key:
275
290
  specification_version: 4
276
291
  summary: Tries really, really hard not to push files to S3.
277
292
  test_files:
278
- - spec/options_spec.rb
293
+ - spec/caching_policy_spec.rb
279
294
  - spec/resource_spec.rb
280
295
  - spec/spec_helper.rb
@@ -1,130 +0,0 @@
1
- require 'spec_helper'
2
- require 'middleman/s3_sync/options'
3
-
4
- describe Middleman::S3Sync::Options do
5
- subject(:options) { Middleman::S3Sync::Options.new }
6
-
7
- its(:delete) { is_expected.to eq(true) }
8
- its(:after_build) { is_expected.to eq(false)}
9
- its(:prefer_gzip) { is_expected.to eq(true) }
10
- its(:aws_secret_access_key) { is_expected.to eq(ENV['AWS_SECRET_ACCESS_KEY']) }
11
- its(:aws_access_key_id) { is_expected.to eq(ENV['AWS_ACCESS_KEY_ID']) }
12
- its(:caching_policies) { is_expected.to be_empty }
13
- its(:default_caching_policy) { is_expected.to be_nil }
14
-
15
- context "browser caching policy" do
16
- let(:policy) { options.default_caching_policy }
17
-
18
- it "should have a blank default caching policy" do
19
- options.add_caching_policy :default, {}
20
-
21
- policy.should_not be_nil
22
-
23
- policy.to_s.should_not =~ /max-age=/
24
- policy.to_s.should_not =~ /s-maxage=/
25
- policy.to_s.should_not =~ /public/
26
- policy.to_s.should_not =~ /private/
27
- policy.to_s.should_not =~ /no-cache/
28
- policy.to_s.should_not =~ /no-store/
29
- policy.to_s.should_not =~ /must-revalidate/
30
- policy.to_s.should_not =~ /proxy-revalidate/
31
- policy.expires.should be_nil
32
- end
33
-
34
- it "should set the max-age policy" do
35
- options.add_caching_policy :default, :max_age => 300
36
-
37
- policy.to_s.should =~ /max-age=300/
38
- end
39
-
40
- it "should set the s-maxage policy" do
41
- options.add_caching_policy :default, :s_maxage => 300
42
-
43
- policy.to_s.should =~ /s-maxage=300/
44
- end
45
-
46
- it "should set the public flag on the policy if set to true" do
47
- options.add_caching_policy :default, :public => true
48
-
49
- policy.to_s.should =~ /public/
50
- end
51
-
52
- it "should not set the public flag on the policy if it is set to false" do
53
- options.add_caching_policy :default, :public => false
54
-
55
- policy.to_s.should_not =~ /public/
56
- end
57
-
58
- it "should set the private flag on the policy if it is set to true" do
59
- options.add_caching_policy :default, :private => true
60
-
61
- policy.to_s.should =~ /private/
62
- end
63
-
64
- it "should set the no-cache flag on the policy if it is set to true" do
65
- options.add_caching_policy :default, :no_cache => true
66
-
67
- policy.to_s.should =~ /no-cache/
68
- end
69
-
70
- it "should set the no-store flag if it is set to true" do
71
- options.add_caching_policy :default, :no_store => true
72
-
73
- policy.to_s.should =~ /no-store/
74
- end
75
-
76
- it "should set the must-revalidate policy if it is set to true" do
77
- options.add_caching_policy :default, :must_revalidate => true
78
-
79
- policy.to_s.should =~ /must-revalidate/
80
- end
81
-
82
- it "should set the proxy-revalidate policy if it is set to true" do
83
- options.add_caching_policy :default, :proxy_revalidate => true
84
-
85
- policy.to_s.should =~ /proxy-revalidate/
86
- end
87
-
88
- it "should divide caching policies with commas and a space" do
89
- options.add_caching_policy :default, :max_age => 300, :public => true
90
-
91
- policies = policy.to_s.split(/, /)
92
- policies.length.should == 2
93
- policies.first.should == 'max-age=300'
94
- policies.last.should == 'public'
95
- end
96
-
97
- it "should set the expiration date" do
98
- expiration = 1.years.from_now
99
-
100
- options.add_caching_policy :default, :expires => expiration
101
- policy.expires.should == CGI.rfc1123_date(expiration)
102
- end
103
- end
104
-
105
- context "#read_config" do
106
- let(:aws_access_key_id) { "foo" }
107
- let(:aws_secret_access_key) { "bar" }
108
- let(:bucket) { "baz" }
109
- let(:config) { { "aws_access_key_id" => aws_access_key_id, "aws_secret_access_key" => aws_secret_access_key, "bucket" => bucket } }
110
- let(:file) { StringIO.new(YAML.dump(config)) }
111
-
112
- before do
113
- options.read_config(file)
114
- end
115
-
116
- its(:aws_access_key_id) { should eq(aws_access_key_id) }
117
- its(:aws_secret_access_key) { should eq(aws_secret_access_key) }
118
- its(:bucket) { should eq(bucket) }
119
- end
120
-
121
- context "prefix with http_prefix" do
122
- before do
123
- options.http_prefix = "/blog"
124
- options.prefix = "blog"
125
- end
126
-
127
- its(:prefix) { should eq("")}
128
- end
129
-
130
- end