middleman-cloudfront 0.2.1 → 0.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
- SHA1:
3
- metadata.gz: 007e6bd92ef3aa1bda2443a2bcb204ff9c629359
4
- data.tar.gz: bbdd3a6b015710a5da1cc00b8f4124645fa53449
2
+ SHA256:
3
+ metadata.gz: 45630e9002202a7bb7480ca73b84675937a5068864f7c238dd2c477899cf9184
4
+ data.tar.gz: 33c8b3c56d298a6f5ea6ff9c3382867b6e684d74617e1de255b90cb5b1744aa9
5
5
  SHA512:
6
- metadata.gz: c86faeaea4789c2962892e8d6ab4b227fc32b4696b3187b9b78dc26694de484b921cda7547a7bdafb1f93ba97d6f2aebef85fa5900409a875cc4b19855caaec1
7
- data.tar.gz: 368a02b61331f1b8f74264825674a3d6cd6f2a998c4521992f91d6d8dd8ba0f3f30df65f627506ee757bfd28efd48df33045e63934e327a13074473941c4a338
6
+ metadata.gz: a09550094d9d1bc154e36fc2ba60beede436c967c93e698f6bd2d99038dd1ea001825689d31e38b4b72cb61bc5869e97d2c8dfe0e644d48a7723753802a7e1d7
7
+ data.tar.gz: dac269c7465ae90b45767a485550906f72eaef4417bfe3fbb4c6f9658578e9cbe245700911ef2164af039c2ffd66fdfaf367451522e9d427702acdc89367f650
@@ -1,7 +1,9 @@
1
1
  language: ruby
2
+ sudo: false
3
+ cache: bundler
4
+ before_script:
5
+ - gem update --system
6
+ - gem update bundler
2
7
  rvm:
3
- - 1.9
4
- - 2.0
5
- - 2.1
6
- - 2.2
7
- - jruby
8
+ - 2.2.5
9
+ - 2.3.1
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Middleman CloudFront [![Build Status](https://travis-ci.org/andrusha/middleman-cloudfront.png)](https://travis-ci.org/andrusha/middleman-cloudfront) [![Dependency Status](https://gemnasium.com/andrusha/middleman-cloudfront.png)](https://gemnasium.com/andrusha/middleman-cloudfront) [![Code Climate](https://codeclimate.com/github/andrusha/middleman-cloudfront.png)](https://codeclimate.com/github/andrusha/middleman-cloudfront)
1
+ # Middleman CloudFront [![Build Status](https://travis-ci.org/andrusha/middleman-cloudfront.svg?branch=master)](https://travis-ci.org/andrusha/middleman-cloudfront) [![Dependency Status](https://gemnasium.com/andrusha/middleman-cloudfront.png)](https://gemnasium.com/andrusha/middleman-cloudfront) [![Code Climate](https://codeclimate.com/github/andrusha/middleman-cloudfront.png)](https://codeclimate.com/github/andrusha/middleman-cloudfront)
2
2
  A deploying tool for middleman which allows you to interact with Amazon CloudFront.
3
3
  Some of its features are:
4
4
 
@@ -115,3 +115,5 @@ after_s3_sync do |files_by_status|
115
115
  invalidate files_by_status[:updated]
116
116
  end
117
117
  ```
118
+
119
+ NOTE: The `after_s3_sync` hook only works with middleman-s3_sync v3.x and below. It has been [removed in v4.0](https://github.com/fredjean/middleman-s3_sync/blob/master/Changelog.md#v400).
data/Rakefile CHANGED
@@ -3,12 +3,6 @@ require 'rspec/core/rake_task'
3
3
  require 'bundler'
4
4
  Bundler::GemHelper.install_tasks
5
5
 
6
- require 'cucumber/rake/task'
7
-
8
- Cucumber::Rake::Task.new(:cucumber, 'Run features that should pass') do |t|
9
- t.cucumber_opts = "--color --tags ~@wip --strict --format #{ENV['CUCUMBER_FORMAT'] || 'Fivemat'}"
10
- end
11
-
12
6
  RSpec::Core::RakeTask.new(:spec)
13
7
 
14
8
  task :default => :spec
@@ -1,7 +1,4 @@
1
- require 'middleman-core'
2
- require 'middleman-cloudfront/commands'
1
+ require 'pathname' # for some reason, had to require this because middleman-core 4.1.7 did not.
2
+ require 'middleman-cloudfront/extension'
3
3
 
4
- ::Middleman::Extensions.register(:cloudfront, ">= 3.0.0") do
5
- require "middleman-cloudfront/extension"
6
- ::Middleman::CloudFront
7
- end
4
+ Middleman::Extensions.register :cloudfront, Middleman::CloudFront::Extension
@@ -0,0 +1,146 @@
1
+ require 'middleman-cli'
2
+ require 'middleman-cloudfront/extension'
3
+ require 'fog/aws'
4
+
5
+ module Middleman
6
+ module Cli
7
+ module CloudFront
8
+ # This class provides an "invalidate" command for the middleman CLI.
9
+ class Invalidate < ::Thor::Group
10
+ include Thor::Actions
11
+
12
+ INVALIDATION_LIMIT = 1000
13
+ INDEX_REGEX = /
14
+ \A
15
+ (.*\/)?
16
+ index\.html
17
+ \z
18
+ /ix
19
+
20
+ check_unknown_options!
21
+
22
+ def self.exit_on_failure?
23
+ true
24
+ end
25
+
26
+ def invalidate(options = nil, files = nil)
27
+
28
+ # If called via commandline, discover config (from bin/middleman)
29
+ if options.nil?
30
+ app = Middleman::Application.new do
31
+ config[:mode] = :config
32
+ config[:exit_before_ready] = true
33
+ config[:watcher_disable] = true
34
+ config[:disable_sitemap] = true
35
+ end
36
+
37
+ # Get the options from the cloudfront extension
38
+ extension = app.extensions[:cloudfront]
39
+ unless extension.nil?
40
+ options = extension.options
41
+ end
42
+ end
43
+
44
+ if options.nil?
45
+ configuration_usage
46
+ end
47
+
48
+ [:distribution_id, :filter].each do |key|
49
+ raise StandardError, "Configuration key #{key} is missing." if options.public_send(key).nil?
50
+ end
51
+
52
+ puts '## Invalidating files on CloudFront'
53
+
54
+ fog_options = {
55
+ :provider => 'AWS'
56
+ }
57
+
58
+ fog_options.merge!(
59
+ if options.access_key_id && options.secret_access_key
60
+ {
61
+ :aws_access_key_id => options.access_key_id,
62
+ :aws_secret_access_key => options.secret_access_key
63
+ }
64
+ else
65
+ { :use_iam_profile => true }
66
+ end
67
+ )
68
+
69
+ cdn = Fog::CDN.new(fog_options)
70
+
71
+ distribution = cdn.distributions.get(options.distribution_id)
72
+
73
+ raise StandardError, "Cannot access Distribution with id #{options.distribution_id}." if distribution.nil?
74
+
75
+
76
+ # CloudFront limits the amount of files which can be invalidated by one request to 1000.
77
+ # If there are more than 1000 files to invalidate, do so sequentially and wait until each validation is ready.
78
+ # If there are max 1000 files, create the invalidation and return immediately.
79
+ files = normalize_files(files || list_files(options.filter))
80
+ return if files.empty?
81
+
82
+ if files.count <= INVALIDATION_LIMIT
83
+ puts "Invalidating #{files.count} files. It might take 10 to 15 minutes until all files are invalidated."
84
+ puts 'Please check the AWS Management Console to see the status of the invalidation.'
85
+ invalidation = distribution.invalidations.create(:paths => files)
86
+ raise StandardError, %(Invalidation status is #{invalidation.status}. Expected "InProgress") unless invalidation.status == 'InProgress'
87
+ else
88
+ slices = files.each_slice(INVALIDATION_LIMIT)
89
+ puts "Invalidating #{files.count} files in #{slices.count} batch(es). It might take 10 to 15 minutes per batch until all files are invalidated."
90
+ slices.each_with_index do |slice, i|
91
+ puts "Invalidating batch #{i + 1}..."
92
+ invalidation = distribution.invalidations.create(:paths => slice)
93
+ invalidation.wait_for { ready? } unless i == slices.count - 1
94
+ end
95
+ end
96
+ end
97
+
98
+ protected
99
+
100
+ def configuration_usage
101
+ raise Error, <<-TEXT
102
+ ERROR: You need to activate the cloudfront extension in config.rb.
103
+
104
+ The example configuration is:
105
+ activate :cloudfront do |cf|
106
+ cf.access_key_id = 'I'
107
+ cf.secret_access_key = 'love'
108
+ cf.distribution_id = 'cats'
109
+ # cf.filter = /\.html/i # default /.*/
110
+ # cf.after_build = true # default is false
111
+ end
112
+ TEXT
113
+ end
114
+
115
+ def list_files(filter)
116
+ Dir.chdir('build/') do
117
+ Dir.glob('**/*', File::FNM_DOTMATCH).tap do |files|
118
+ # Remove directories
119
+ files.reject! { |f| File.directory?(f) }
120
+
121
+ # Remove files that do not match filter
122
+ files.reject! { |f| f !~ filter }
123
+ end
124
+ end
125
+ end
126
+
127
+ def normalize_files(files)
128
+ # Add directories since they have to be invalidated
129
+ # as well if :directory_indexes is active
130
+ files += files.grep(INDEX_REGEX).map do |file|
131
+ file == 'index.html' ? '/' : File.dirname(file) << '/'
132
+ end.uniq
133
+
134
+ # URI encode and add leading slash
135
+ files.map { |f| URI::encode(f.start_with?('/') ? f : "/#{f}") }
136
+ end
137
+
138
+ # Add to CLI
139
+ Base.register(self, 'invalidate', 'invalidate', 'Invalidate a cloudfront distribution.')
140
+
141
+ # Map "inv" to "invalidate"
142
+ Base.map('inv' => 'invalidate')
143
+ end
144
+ end
145
+ end
146
+ end
@@ -1,36 +1,31 @@
1
1
  require 'middleman-core'
2
+ require 'middleman-cloudfront/commands/invalidate'
2
3
 
3
4
  module Middleman
4
5
  module CloudFront
5
- class Options < Struct.new(:access_key_id, :secret_access_key, :distribution_id, :filter, :after_build); end
6
-
7
- class << self
8
- def options
9
- @@cloudfront_options
6
+ class Extension < Middleman::Extension
7
+ # @param [Symbol] key The name of the option
8
+ # @param [Object] default The default value for the option
9
+ # @param [String] description A human-readable description of what the option does
10
+ option :access_key_id, nil, 'Access key id'
11
+ option :secret_access_key, nil, 'Secret access key'
12
+ option :distribution_id, nil, 'Distribution id'
13
+ option :filter, /.*/, 'Filter files to be invalidated'
14
+ option :after_build, false, 'Invalidate after build'
15
+
16
+ def initialize(app, options_hash={}, &block)
17
+ super
10
18
  end
11
19
 
12
- def registered(app, options_hash = {}, &block)
13
- @@cloudfront_options = Options.new(options_hash)
14
- yield @@cloudfront_options if block_given?
15
-
16
- app.after_build do
17
- ::Middleman::Cli::CloudFront.new.invalidate(@@cloudfront_options) if @@cloudfront_options.after_build
18
- end
19
-
20
- app.send :include, Helpers
20
+ def after_build
21
+ Middleman::Cli::CloudFront::Invalidate.new.invalidate(options) if options.after_build
21
22
  end
22
- alias :included :registered
23
- end
24
23
 
25
- module Helpers
26
- def cloudfront_options
27
- ::Middleman::CloudFront.options
28
- end
29
-
30
- def invalidate(files = nil)
31
- ::Middleman::Cli::CloudFront.new.invalidate(cloudfront_options, files)
24
+ helpers do
25
+ def invalidate(files = nil)
26
+ Middleman::Cli::CloudFront::Invalidate.new.invalidate(options, files)
27
+ end
32
28
  end
33
29
  end
34
-
35
30
  end
36
31
  end
@@ -1,5 +1,5 @@
1
1
  module Middleman
2
2
  module CloudFront
3
- VERSION = '0.2.1'
3
+ VERSION = '0.3.0'
4
4
  end
5
5
  end
@@ -15,25 +15,16 @@ Gem::Specification.new do |s|
15
15
  s.summary = %q{Invalidate CloudFront cache after deployment to S3}
16
16
  s.description = %q{Adds ability to invalidate a specific set of files in your CloudFront cache}
17
17
 
18
- s.rubyforge_project = "middleman-cloudfront"
19
-
20
18
  s.files = `git ls-files -z`.split("\0")
21
19
  s.test_files = `git ls-files -z -- {fixtures,features}/*`.split("\0")
22
20
  s.require_paths = ["lib"]
23
21
 
24
- s.add_dependency 'fog', '~> 1.9'
22
+ s.required_ruby_version = '>= 2.0.0'
25
23
 
26
- s.add_development_dependency 'cucumber', '~> 1.3'
27
- s.add_development_dependency 'aruba', '~> 0.5'
28
- s.add_development_dependency 'fivemat', '~> 1.3'
29
- s.add_development_dependency 'simplecov', '~> 0.8'
30
24
  s.add_development_dependency 'rake', '>= 0.9.0'
31
25
  s.add_development_dependency 'rspec', '~> 3.0'
32
26
 
33
- if RUBY_VERSION <= '1.9.2'
34
- s.add_dependency 'middleman-core', '~> 3.0', '<= 3.2.0'
35
- s.add_development_dependency 'activesupport', '< 4.0.0'
36
- else
37
- s.add_dependency 'middleman-core', '~> 3.0'
38
- end
27
+ s.add_dependency 'fog-aws', '>= 0.1.1'
28
+ s.add_dependency 'middleman-core', '>= 3.0'
29
+ s.add_dependency 'middleman-cli', '>= 3.0'
39
30
  end
@@ -0,0 +1,89 @@
1
+ require 'spec_helper'
2
+
3
+ require 'fog/aws/models/cdn/distributions'
4
+
5
+ describe Middleman::Cli::CloudFront::Invalidate do
6
+ INVALIDATION_LIMIT = Middleman::Cli::CloudFront::Invalidate::INVALIDATION_LIMIT
7
+ let(:cloudfront) { described_class.new }
8
+ let(:options) do
9
+ config = Middleman::Configuration::ConfigurationManager.new
10
+ config.access_key_id ='access_key_id_123'
11
+ config.secret_access_key = 'secret_access_key_123'
12
+ config.distribution_id = 'distribution_id_123'
13
+ config.filter = 'filter_123'
14
+ config.after_build = 'after_build_123'
15
+ config
16
+ end
17
+ let(:distribution) { double('distribution', invalidations: double('invalidations')) }
18
+
19
+ before do
20
+ allow_any_instance_of(Fog::CDN::AWS::Distributions).to receive(:get).and_return(distribution)
21
+ allow(distribution.invalidations).to receive(:create) do
22
+ double('invalidation', status: 'InProgress', wait_for: -> {})
23
+ end
24
+ end
25
+
26
+ it 'gets the correct distribution' do
27
+ allow(cloudfront).to receive(:list_files).and_return([])
28
+ expect_any_instance_of(Fog::CDN::AWS::Distributions).to receive(:get).with('distribution_id_123')
29
+ cloudfront.invalidate(options)
30
+ end
31
+
32
+ it 'normalizes paths' do
33
+ files = %w(file directory/index.html)
34
+ normalized_files = %w(/file /directory/index.html /directory/)
35
+ allow(cloudfront).to receive(:list_files).and_return(files)
36
+ expect(distribution.invalidations).to receive(:create).once.with(paths: normalized_files)
37
+ cloudfront.invalidate(options)
38
+ end
39
+
40
+ context 'when the amount of files to invalidate is under the limit' do
41
+ it 'divides them up in packages and creates one invalidation per package' do
42
+ files = (1..INVALIDATION_LIMIT).map { |i| "/file_#{i}" }
43
+ allow(cloudfront).to receive(:list_files).and_return(files)
44
+ expect(distribution.invalidations).to receive(:create).once.with(paths: files)
45
+ cloudfront.invalidate(options)
46
+ end
47
+ end
48
+
49
+ context 'when the amount of files to invalidate is over the limit' do
50
+ it 'creates only one invalidation with all of them' do
51
+ files = (1..(INVALIDATION_LIMIT * 3)).map { |i| "/file_#{i}" }
52
+ allow(cloudfront).to receive(:list_files).and_return(files)
53
+ expect(distribution.invalidations).to receive(:create).once.with(paths: files[0, INVALIDATION_LIMIT])
54
+ expect(distribution.invalidations).to receive(:create).once.with(paths: files[INVALIDATION_LIMIT, INVALIDATION_LIMIT])
55
+ expect(distribution.invalidations).to receive(:create).once.with(paths: files[INVALIDATION_LIMIT * 2, INVALIDATION_LIMIT])
56
+ cloudfront.invalidate(options)
57
+ end
58
+ end
59
+
60
+ context 'when files to invalidate are explicitly specified' do
61
+ it 'uses them instead of the files in the build directory' do
62
+ files = (1..3).map { |i| "/file_#{i}" }
63
+ expect(distribution.invalidations).to receive(:create).once.with(paths: files)
64
+ cloudfront.invalidate(options, files)
65
+ end
66
+
67
+ it "doesn't filter them" do
68
+ files = (1..3).map { |i| "/file_#{i}" }
69
+ options.filter = /filter that matches no files/
70
+ expect(distribution.invalidations).to receive(:create).once.with(paths: files)
71
+ cloudfront.invalidate(options, files)
72
+ end
73
+
74
+ it 'normalizes them' do
75
+ files = %w(file directory/index.html)
76
+ normalized_files = %w(/file /directory/index.html /directory/)
77
+ expect(distribution.invalidations).to receive(:create).once.with(paths: normalized_files)
78
+ cloudfront.invalidate(options, files)
79
+ end
80
+ end
81
+
82
+ context 'when the distribution_id is invalid' do
83
+ it 'it raise_error' do
84
+ #fog will return nil if the distribution_id is invalid
85
+ allow_any_instance_of(Fog::CDN::AWS::Distributions).to receive(:get).and_return(nil)
86
+ expect { cloudfront.invalidate(options) }.to raise_error(StandardError, "Cannot access Distribution with id distribution_id_123.")
87
+ end
88
+ end
89
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: middleman-cloudfront
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Korzhuev
@@ -9,118 +9,76 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-04-18 00:00:00.000000000 Z
12
+ date: 2017-12-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: fog
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - "~>"
19
- - !ruby/object:Gem::Version
20
- version: '1.9'
21
- type: :runtime
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - "~>"
26
- - !ruby/object:Gem::Version
27
- version: '1.9'
28
- - !ruby/object:Gem::Dependency
29
- name: cucumber
30
- requirement: !ruby/object:Gem::Requirement
31
- requirements:
32
- - - "~>"
33
- - !ruby/object:Gem::Version
34
- version: '1.3'
35
- type: :development
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - "~>"
40
- - !ruby/object:Gem::Version
41
- version: '1.3'
42
- - !ruby/object:Gem::Dependency
43
- name: aruba
44
- requirement: !ruby/object:Gem::Requirement
45
- requirements:
46
- - - "~>"
47
- - !ruby/object:Gem::Version
48
- version: '0.5'
49
- type: :development
50
- prerelease: false
51
- version_requirements: !ruby/object:Gem::Requirement
52
- requirements:
53
- - - "~>"
54
- - !ruby/object:Gem::Version
55
- version: '0.5'
56
- - !ruby/object:Gem::Dependency
57
- name: fivemat
15
+ name: rake
58
16
  requirement: !ruby/object:Gem::Requirement
59
17
  requirements:
60
- - - "~>"
18
+ - - ">="
61
19
  - !ruby/object:Gem::Version
62
- version: '1.3'
20
+ version: 0.9.0
63
21
  type: :development
64
22
  prerelease: false
65
23
  version_requirements: !ruby/object:Gem::Requirement
66
24
  requirements:
67
- - - "~>"
25
+ - - ">="
68
26
  - !ruby/object:Gem::Version
69
- version: '1.3'
27
+ version: 0.9.0
70
28
  - !ruby/object:Gem::Dependency
71
- name: simplecov
29
+ name: rspec
72
30
  requirement: !ruby/object:Gem::Requirement
73
31
  requirements:
74
32
  - - "~>"
75
33
  - !ruby/object:Gem::Version
76
- version: '0.8'
34
+ version: '3.0'
77
35
  type: :development
78
36
  prerelease: false
79
37
  version_requirements: !ruby/object:Gem::Requirement
80
38
  requirements:
81
39
  - - "~>"
82
40
  - !ruby/object:Gem::Version
83
- version: '0.8'
41
+ version: '3.0'
84
42
  - !ruby/object:Gem::Dependency
85
- name: rake
43
+ name: fog-aws
86
44
  requirement: !ruby/object:Gem::Requirement
87
45
  requirements:
88
46
  - - ">="
89
47
  - !ruby/object:Gem::Version
90
- version: 0.9.0
91
- type: :development
48
+ version: 0.1.1
49
+ type: :runtime
92
50
  prerelease: false
93
51
  version_requirements: !ruby/object:Gem::Requirement
94
52
  requirements:
95
53
  - - ">="
96
54
  - !ruby/object:Gem::Version
97
- version: 0.9.0
55
+ version: 0.1.1
98
56
  - !ruby/object:Gem::Dependency
99
- name: rspec
57
+ name: middleman-core
100
58
  requirement: !ruby/object:Gem::Requirement
101
59
  requirements:
102
- - - "~>"
60
+ - - ">="
103
61
  - !ruby/object:Gem::Version
104
62
  version: '3.0'
105
- type: :development
63
+ type: :runtime
106
64
  prerelease: false
107
65
  version_requirements: !ruby/object:Gem::Requirement
108
66
  requirements:
109
- - - "~>"
67
+ - - ">="
110
68
  - !ruby/object:Gem::Version
111
69
  version: '3.0'
112
70
  - !ruby/object:Gem::Dependency
113
- name: middleman-core
71
+ name: middleman-cli
114
72
  requirement: !ruby/object:Gem::Requirement
115
73
  requirements:
116
- - - "~>"
74
+ - - ">="
117
75
  - !ruby/object:Gem::Version
118
76
  version: '3.0'
119
77
  type: :runtime
120
78
  prerelease: false
121
79
  version_requirements: !ruby/object:Gem::Requirement
122
80
  requirements:
123
- - - "~>"
81
+ - - ">="
124
82
  - !ruby/object:Gem::Version
125
83
  version: '3.0'
126
84
  description: Adds ability to invalidate a specific set of files in your CloudFront
@@ -138,14 +96,13 @@ files:
138
96
  - LICENSE
139
97
  - README.md
140
98
  - Rakefile
141
- - features/support/env.rb
142
99
  - lib/middleman-cloudfront.rb
143
- - lib/middleman-cloudfront/commands.rb
100
+ - lib/middleman-cloudfront/commands/invalidate.rb
144
101
  - lib/middleman-cloudfront/extension.rb
145
102
  - lib/middleman-cloudfront/version.rb
146
103
  - lib/middleman_extension.rb
147
104
  - middleman-cloudfront.gemspec
148
- - spec/lib/middleman-cloudfront/commands_spec.rb
105
+ - spec/lib/middleman-cloudfront/invalidate_spec.rb
149
106
  - spec/spec_helper.rb
150
107
  homepage: https://github.com/andrusha/middleman-cloudfront
151
108
  licenses: []
@@ -158,18 +115,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
158
115
  requirements:
159
116
  - - ">="
160
117
  - !ruby/object:Gem::Version
161
- version: '0'
118
+ version: 2.0.0
162
119
  required_rubygems_version: !ruby/object:Gem::Requirement
163
120
  requirements:
164
121
  - - ">="
165
122
  - !ruby/object:Gem::Version
166
123
  version: '0'
167
124
  requirements: []
168
- rubyforge_project: middleman-cloudfront
169
- rubygems_version: 2.4.5
125
+ rubyforge_project:
126
+ rubygems_version: 2.7.4
170
127
  signing_key:
171
128
  specification_version: 4
172
129
  summary: Invalidate CloudFront cache after deployment to S3
173
- test_files:
174
- - features/support/env.rb
175
- has_rdoc:
130
+ test_files: []
@@ -1,6 +0,0 @@
1
- require "simplecov"
2
- SimpleCov.start
3
- PROJECT_ROOT_PATH = File.dirname(File.dirname(File.dirname(__FILE__)))
4
- require "middleman-core"
5
- require "middleman-core/step_definitions"
6
- require File.join(PROJECT_ROOT_PATH, 'lib', 'middleman-cloudfront')
@@ -1,122 +0,0 @@
1
- require "middleman-core/cli"
2
- require "middleman-cloudfront/extension"
3
- require "fog"
4
-
5
- module Middleman
6
- module Cli
7
-
8
- class CloudFront < Thor
9
- include Thor::Actions
10
-
11
- INVALIDATION_LIMIT = 1000
12
- INDEX_REGEX = /
13
- \A
14
- (.*\/)?
15
- index\.html
16
- \z
17
- /ix
18
-
19
- check_unknown_options!
20
-
21
- namespace :invalidate
22
-
23
- def self.exit_on_failure?
24
- true
25
- end
26
-
27
- desc "invalidate", "A way to deal with your CloudFront distributions"
28
- def invalidate(options = nil, files = nil)
29
- if options.nil?
30
- app_instance = ::Middleman::Application.server.inst
31
- unless app_instance.respond_to?(:cloudfront_options)
32
- raise Error, <<-TEXT
33
- ERROR: You need to activate the cloudfront extension in config.rb.
34
-
35
- The example configuration is:
36
- activate :cloudfront do |cf|
37
- cf.access_key_id = 'I'
38
- cf.secret_access_key = 'love'
39
- cf.distribution_id = 'cats'
40
- cf.filter = /\.html/i # default /.*/
41
- cf.after_build = true # default is false
42
- end
43
- TEXT
44
- end
45
- options = app_instance.cloudfront_options
46
- end
47
- options.filter ||= /.*/
48
- [:distribution_id, :filter].each do |key|
49
- raise StandardError, "Configuration key #{key} is missing." if options.public_send(key).nil?
50
- end
51
-
52
- puts "## Invalidating files on CloudFront"
53
-
54
- fog_options = {
55
- :provider => 'AWS'
56
- }
57
-
58
- if options.access_key_id && options.secret_access_key
59
- fog_options.merge!({
60
- :aws_access_key_id => options.access_key_id,
61
- :aws_secret_access_key => options.secret_access_key
62
- })
63
- else
64
- fog_options.merge!({:use_iam_profile => true})
65
- end
66
-
67
- cdn = Fog::CDN.new(fog_options)
68
-
69
- distribution = cdn.distributions.get(options.distribution_id)
70
-
71
- # CloudFront limits the amount of files which can be invalidated by one request to 1000.
72
- # If there are more than 1000 files to invalidate, do so sequentially and wait until each validation is ready.
73
- # If there are max 1000 files, create the invalidation and return immediately.
74
- files = normalize_files(files || list_files(options.filter))
75
- return if files.empty?
76
-
77
- if files.count <= INVALIDATION_LIMIT
78
- puts "Invalidating #{files.count} files. It might take 10 to 15 minutes until all files are invalidated."
79
- puts 'Please check the AWS Management Console to see the status of the invalidation.'
80
- invalidation = distribution.invalidations.create(:paths => files)
81
- raise StandardError, %(Invalidation status is #{invalidation.status}. Expected "InProgress") unless invalidation.status == 'InProgress'
82
- else
83
- slices = files.each_slice(INVALIDATION_LIMIT)
84
- puts "Invalidating #{files.count} files in #{slices.count} batch(es). It might take 10 to 15 minutes per batch until all files are invalidated."
85
- slices.each_with_index do |slice, i|
86
- puts "Invalidating batch #{i + 1}..."
87
- invalidation = distribution.invalidations.create(:paths => slice)
88
- invalidation.wait_for { ready? } unless i == slices.count - 1
89
- end
90
- end
91
- end
92
-
93
- protected
94
-
95
- def list_files(filter)
96
- Dir.chdir('build/') do
97
- Dir.glob('**/*', File::FNM_DOTMATCH).tap do |files|
98
- # Remove directories
99
- files.reject! { |f| File.directory?(f) }
100
-
101
- # Remove files that do not match filter
102
- files.reject! { |f| f !~ filter }
103
- end
104
- end
105
- end
106
-
107
- def normalize_files(files)
108
- # Add directories since they have to be invalidated
109
- # as well if :directory_indexes is active
110
- files += files.grep(INDEX_REGEX).map do |file|
111
- file == 'index.html' ? '/' : File.dirname(file) << '/'
112
- end.uniq
113
-
114
- # URI encode and add leading slash
115
- files.map { |f| URI::encode(f.start_with?('/') ? f : "/#{f}") }
116
- end
117
-
118
- end
119
-
120
- Base.map({"inv" => "invalidate"})
121
- end
122
- end
@@ -1,82 +0,0 @@
1
- require 'spec_helper'
2
-
3
- require 'fog/aws/models/cdn/distributions'
4
-
5
- describe Middleman::Cli::CloudFront do
6
- let(:cloudfront) { described_class.new }
7
- let(:options) do
8
- Middleman::CloudFront::Options.new(
9
- 'access_key_id_123',
10
- 'secret_access_key_123',
11
- 'distribution_id_123',
12
- 'filter_123',
13
- 'after_build_123'
14
- )
15
- end
16
- let(:distribution) { double('distribution', invalidations: double('invalidations')) }
17
-
18
- describe '#invalidate' do
19
- before do
20
- allow_any_instance_of(Fog::CDN::AWS::Distributions).to receive(:get).and_return(distribution)
21
- allow(distribution.invalidations).to receive(:create) do
22
- double('invalidation', status: 'InProgress', wait_for: -> {})
23
- end
24
- end
25
-
26
- it 'gets the correct distribution' do
27
- allow(cloudfront).to receive(:list_files).and_return([])
28
- expect_any_instance_of(Fog::CDN::AWS::Distributions).to receive(:get).with('distribution_id_123')
29
- cloudfront.invalidate(options)
30
- end
31
-
32
- it 'normalizes paths' do
33
- files = %w(file directory/index.html)
34
- normalized_files = %w(/file /directory/index.html /directory/)
35
- allow(cloudfront).to receive(:list_files).and_return(files)
36
- expect(distribution.invalidations).to receive(:create).once.with(paths: normalized_files)
37
- cloudfront.invalidate(options)
38
- end
39
-
40
- context 'when the amount of files to invalidate is under the limit' do
41
- it 'divides them up in packages and creates one invalidation per package' do
42
- files = (1..Middleman::Cli::CloudFront::INVALIDATION_LIMIT).map { |i| "/file_#{i}" }
43
- allow(cloudfront).to receive(:list_files).and_return(files)
44
- expect(distribution.invalidations).to receive(:create).once.with(paths: files)
45
- cloudfront.invalidate(options)
46
- end
47
- end
48
-
49
- context 'when the amount of files to invalidate is over the limit' do
50
- it 'creates only one invalidation with all of them' do
51
- files = (1..(Middleman::Cli::CloudFront::INVALIDATION_LIMIT * 3)).map { |i| "/file_#{i}" }
52
- allow(cloudfront).to receive(:list_files).and_return(files)
53
- expect(distribution.invalidations).to receive(:create).once.with(paths: files[0, Middleman::Cli::CloudFront::INVALIDATION_LIMIT])
54
- expect(distribution.invalidations).to receive(:create).once.with(paths: files[Middleman::Cli::CloudFront::INVALIDATION_LIMIT, Middleman::Cli::CloudFront::INVALIDATION_LIMIT])
55
- expect(distribution.invalidations).to receive(:create).once.with(paths: files[Middleman::Cli::CloudFront::INVALIDATION_LIMIT * 2, Middleman::Cli::CloudFront::INVALIDATION_LIMIT])
56
- cloudfront.invalidate(options)
57
- end
58
- end
59
-
60
- context 'when files to invalidate are explicitly specified' do
61
- it 'uses them instead of the files in the build directory' do
62
- files = (1..3).map { |i| "/file_#{i}" }
63
- expect(distribution.invalidations).to receive(:create).once.with(paths: files)
64
- cloudfront.invalidate(options, files)
65
- end
66
-
67
- it "doesn't filter them" do
68
- files = (1..3).map { |i| "/file_#{i}" }
69
- options.filter = /filter that matches no files/
70
- expect(distribution.invalidations).to receive(:create).once.with(paths: files)
71
- cloudfront.invalidate(options, files)
72
- end
73
-
74
- it 'normalizes them' do
75
- files = %w(file directory/index.html)
76
- normalized_files = %w(/file /directory/index.html /directory/)
77
- expect(distribution.invalidations).to receive(:create).once.with(paths: normalized_files)
78
- cloudfront.invalidate(options, files)
79
- end
80
- end
81
- end
82
- end