middleman-s3_sync 3.0.47 → 3.3.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.
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