middleman-cloudfront 0.2.1 → 0.4.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
- SHA1:
3
- metadata.gz: 007e6bd92ef3aa1bda2443a2bcb204ff9c629359
4
- data.tar.gz: bbdd3a6b015710a5da1cc00b8f4124645fa53449
2
+ SHA256:
3
+ metadata.gz: d747f88048b94a728aae2e9f7a5be6edf7555a30d931da06e64292a7ec9647c3
4
+ data.tar.gz: fae578477731498e1b3d520052ea39b87d9d32b47837492867d90e48d0d1ec72
5
5
  SHA512:
6
- metadata.gz: c86faeaea4789c2962892e8d6ab4b227fc32b4696b3187b9b78dc26694de484b921cda7547a7bdafb1f93ba97d6f2aebef85fa5900409a875cc4b19855caaec1
7
- data.tar.gz: 368a02b61331f1b8f74264825674a3d6cd6f2a998c4521992f91d6d8dd8ba0f3f30df65f627506ee757bfd28efd48df33045e63934e327a13074473941c4a338
6
+ metadata.gz: aa5e3a1ae12a5f96752154a2a51e3e56e4e3715b53f193b54fcfe4ff4474912e9b07d26524b7793aaa0307a0854c407e86e5fd649773121d6a5dd01740f7098c
7
+ data.tar.gz: 0c3e070652bfa6a7d797abdb842fb8fc80bad9a96ec3783d0bc12a752723c44de73225ecab6cd534d15826ec85079a060502e77f595dcfe9643638d955c7ad59
@@ -0,0 +1,20 @@
1
+ name: CI / CD
2
+
3
+ on:
4
+ push:
5
+
6
+ jobs:
7
+ test:
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ os: [ubuntu-latest, macos-latest]
12
+ ruby: ['2.2', '2.7', '3.0', '3.1', head]
13
+ runs-on: ${{ matrix.os }}
14
+ steps:
15
+ - uses: actions/checkout@v3
16
+ - uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: ${{ matrix.ruby }}
19
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
20
+ - run: bundle exec rake
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.2.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
@@ -0,0 +1,147 @@
1
+ require 'middleman-cli'
2
+ require 'middleman-cloudfront/extension'
3
+ require 'fog/aws'
4
+ require 'addressable/uri'
5
+
6
+ module Middleman
7
+ module Cli
8
+ module CloudFront
9
+ # This class provides an "invalidate" command for the middleman CLI.
10
+ class Invalidate < ::Thor::Group
11
+ include Thor::Actions
12
+
13
+ INVALIDATION_LIMIT = 1000
14
+ INDEX_REGEX = /
15
+ \A
16
+ (.*\/)?
17
+ index\.html
18
+ \z
19
+ /ix
20
+
21
+ check_unknown_options!
22
+
23
+ def self.exit_on_failure?
24
+ true
25
+ end
26
+
27
+ def invalidate(options = nil, files = nil)
28
+
29
+ # If called via commandline, discover config (from bin/middleman)
30
+ if options.nil?
31
+ app = Middleman::Application.new do
32
+ config[:mode] = :config
33
+ config[:exit_before_ready] = true
34
+ config[:watcher_disable] = true
35
+ config[:disable_sitemap] = true
36
+ end
37
+
38
+ # Get the options from the cloudfront extension
39
+ extension = app.extensions[:cloudfront]
40
+ unless extension.nil?
41
+ options = extension.options
42
+ end
43
+ end
44
+
45
+ if options.nil?
46
+ configuration_usage
47
+ end
48
+
49
+ [:distribution_id, :filter].each do |key|
50
+ raise StandardError, "Configuration key #{key} is missing." if options.public_send(key).nil?
51
+ end
52
+
53
+ puts '## Invalidating files on CloudFront'
54
+
55
+ fog_options = {
56
+ :provider => 'AWS'
57
+ }
58
+
59
+ fog_options.merge!(
60
+ if options.access_key_id && options.secret_access_key
61
+ {
62
+ :aws_access_key_id => options.access_key_id,
63
+ :aws_secret_access_key => options.secret_access_key
64
+ }
65
+ else
66
+ { :use_iam_profile => true }
67
+ end
68
+ )
69
+
70
+ cdn = Fog::CDN.new(fog_options)
71
+
72
+ distribution = cdn.distributions.get(options.distribution_id)
73
+
74
+ raise StandardError, "Cannot access Distribution with id #{options.distribution_id}." if distribution.nil?
75
+
76
+
77
+ # CloudFront limits the amount of files which can be invalidated by one request to 1000.
78
+ # If there are more than 1000 files to invalidate, do so sequentially and wait until each validation is ready.
79
+ # If there are max 1000 files, create the invalidation and return immediately.
80
+ files = normalize_files(files || list_files(options.filter))
81
+ return if files.empty?
82
+
83
+ if files.count <= INVALIDATION_LIMIT
84
+ puts "Invalidating #{files.count} files. It might take 10 to 15 minutes until all files are invalidated."
85
+ puts 'Please check the AWS Management Console to see the status of the invalidation.'
86
+ invalidation = distribution.invalidations.create(:paths => files)
87
+ raise StandardError, %(Invalidation status is #{invalidation.status}. Expected "InProgress") unless invalidation.status == 'InProgress'
88
+ else
89
+ slices = files.each_slice(INVALIDATION_LIMIT)
90
+ puts "Invalidating #{files.count} files in #{slices.count} batch(es). It might take 10 to 15 minutes per batch until all files are invalidated."
91
+ slices.each_with_index do |slice, i|
92
+ puts "Invalidating batch #{i + 1}..."
93
+ invalidation = distribution.invalidations.create(:paths => slice)
94
+ invalidation.wait_for { ready? } unless i == slices.count - 1
95
+ end
96
+ end
97
+ end
98
+
99
+ protected
100
+
101
+ def configuration_usage
102
+ raise Error, <<-TEXT
103
+ ERROR: You need to activate the cloudfront extension in config.rb.
104
+
105
+ The example configuration is:
106
+ activate :cloudfront do |cf|
107
+ cf.access_key_id = 'I'
108
+ cf.secret_access_key = 'love'
109
+ cf.distribution_id = 'cats'
110
+ # cf.filter = /\.html/i # default /.*/
111
+ # cf.after_build = true # default is false
112
+ end
113
+ TEXT
114
+ end
115
+
116
+ def list_files(filter)
117
+ Dir.chdir('build/') do
118
+ Dir.glob('**/*', File::FNM_DOTMATCH).tap do |files|
119
+ # Remove directories
120
+ files.reject! { |f| File.directory?(f) }
121
+
122
+ # Remove files that do not match filter
123
+ files.reject! { |f| f !~ filter }
124
+ end
125
+ end
126
+ end
127
+
128
+ def normalize_files(files)
129
+ # Add directories since they have to be invalidated
130
+ # as well if :directory_indexes is active
131
+ files += files.grep(INDEX_REGEX).map do |file|
132
+ file == 'index.html' ? '/' : File.dirname(file) << '/'
133
+ end.uniq
134
+
135
+ # URI encode and add leading slash
136
+ files.map { |f| Addressable::URI::encode(f.start_with?('/') ? f : "/#{f}") }
137
+ end
138
+
139
+ # Add to CLI
140
+ Base.register(self, 'invalidate', 'invalidate', 'Invalidate a cloudfront distribution.')
141
+
142
+ # Map "inv" to "invalidate"
143
+ Base.map('inv' => 'invalidate')
144
+ end
145
+ end
146
+ end
147
+ 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.4.0'
4
4
  end
5
5
  end
@@ -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
@@ -15,25 +15,17 @@ 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 'addressable', '>= 2.8.1'
28
+ s.add_dependency 'fog-aws', '>= 0.1.1'
29
+ s.add_dependency 'middleman-core', '>= 3.0'
30
+ s.add_dependency 'middleman-cli', '>= 3.0'
39
31
  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,126 +1,98 @@
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.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrey Korzhuev
8
8
  - Manuel Meurer
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-04-18 00:00:00.000000000 Z
12
+ date: 2023-03-30 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
15
+ name: rake
44
16
  requirement: !ruby/object:Gem::Requirement
45
17
  requirements:
46
- - - "~>"
18
+ - - ">="
47
19
  - !ruby/object:Gem::Version
48
- version: '0.5'
20
+ version: 0.9.0
49
21
  type: :development
50
22
  prerelease: false
51
23
  version_requirements: !ruby/object:Gem::Requirement
52
24
  requirements:
53
- - - "~>"
25
+ - - ">="
54
26
  - !ruby/object:Gem::Version
55
- version: '0.5'
27
+ version: 0.9.0
56
28
  - !ruby/object:Gem::Dependency
57
- name: fivemat
29
+ name: rspec
58
30
  requirement: !ruby/object:Gem::Requirement
59
31
  requirements:
60
32
  - - "~>"
61
33
  - !ruby/object:Gem::Version
62
- version: '1.3'
34
+ version: '3.0'
63
35
  type: :development
64
36
  prerelease: false
65
37
  version_requirements: !ruby/object:Gem::Requirement
66
38
  requirements:
67
39
  - - "~>"
68
40
  - !ruby/object:Gem::Version
69
- version: '1.3'
41
+ version: '3.0'
70
42
  - !ruby/object:Gem::Dependency
71
- name: simplecov
43
+ name: addressable
72
44
  requirement: !ruby/object:Gem::Requirement
73
45
  requirements:
74
- - - "~>"
46
+ - - ">="
75
47
  - !ruby/object:Gem::Version
76
- version: '0.8'
77
- type: :development
48
+ version: 2.8.1
49
+ type: :runtime
78
50
  prerelease: false
79
51
  version_requirements: !ruby/object:Gem::Requirement
80
52
  requirements:
81
- - - "~>"
53
+ - - ">="
82
54
  - !ruby/object:Gem::Version
83
- version: '0.8'
55
+ version: 2.8.1
84
56
  - !ruby/object:Gem::Dependency
85
- name: rake
57
+ name: fog-aws
86
58
  requirement: !ruby/object:Gem::Requirement
87
59
  requirements:
88
60
  - - ">="
89
61
  - !ruby/object:Gem::Version
90
- version: 0.9.0
91
- type: :development
62
+ version: 0.1.1
63
+ type: :runtime
92
64
  prerelease: false
93
65
  version_requirements: !ruby/object:Gem::Requirement
94
66
  requirements:
95
67
  - - ">="
96
68
  - !ruby/object:Gem::Version
97
- version: 0.9.0
69
+ version: 0.1.1
98
70
  - !ruby/object:Gem::Dependency
99
- name: rspec
71
+ name: middleman-core
100
72
  requirement: !ruby/object:Gem::Requirement
101
73
  requirements:
102
- - - "~>"
74
+ - - ">="
103
75
  - !ruby/object:Gem::Version
104
76
  version: '3.0'
105
- type: :development
77
+ type: :runtime
106
78
  prerelease: false
107
79
  version_requirements: !ruby/object:Gem::Requirement
108
80
  requirements:
109
- - - "~>"
81
+ - - ">="
110
82
  - !ruby/object:Gem::Version
111
83
  version: '3.0'
112
84
  - !ruby/object:Gem::Dependency
113
- name: middleman-core
85
+ name: middleman-cli
114
86
  requirement: !ruby/object:Gem::Requirement
115
87
  requirements:
116
- - - "~>"
88
+ - - ">="
117
89
  - !ruby/object:Gem::Version
118
90
  version: '3.0'
119
91
  type: :runtime
120
92
  prerelease: false
121
93
  version_requirements: !ruby/object:Gem::Requirement
122
94
  requirements:
123
- - - "~>"
95
+ - - ">="
124
96
  - !ruby/object:Gem::Version
125
97
  version: '3.0'
126
98
  description: Adds ability to invalidate a specific set of files in your CloudFront
@@ -131,26 +103,26 @@ executables: []
131
103
  extensions: []
132
104
  extra_rdoc_files: []
133
105
  files:
106
+ - ".github/workflows/ci_cd.yml"
134
107
  - ".gitignore"
135
- - ".travis.yml"
108
+ - ".tool-versions"
136
109
  - Gemfile
137
110
  - Guardfile
138
111
  - LICENSE
139
112
  - README.md
140
113
  - Rakefile
141
- - features/support/env.rb
142
114
  - lib/middleman-cloudfront.rb
143
- - lib/middleman-cloudfront/commands.rb
115
+ - lib/middleman-cloudfront/commands/invalidate.rb
144
116
  - lib/middleman-cloudfront/extension.rb
145
117
  - lib/middleman-cloudfront/version.rb
146
118
  - lib/middleman_extension.rb
147
119
  - middleman-cloudfront.gemspec
148
- - spec/lib/middleman-cloudfront/commands_spec.rb
120
+ - spec/lib/middleman-cloudfront/invalidate_spec.rb
149
121
  - spec/spec_helper.rb
150
122
  homepage: https://github.com/andrusha/middleman-cloudfront
151
123
  licenses: []
152
124
  metadata: {}
153
- post_install_message:
125
+ post_install_message:
154
126
  rdoc_options: []
155
127
  require_paths:
156
128
  - lib
@@ -158,18 +130,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
158
130
  requirements:
159
131
  - - ">="
160
132
  - !ruby/object:Gem::Version
161
- version: '0'
133
+ version: 2.0.0
162
134
  required_rubygems_version: !ruby/object:Gem::Requirement
163
135
  requirements:
164
136
  - - ">="
165
137
  - !ruby/object:Gem::Version
166
138
  version: '0'
167
139
  requirements: []
168
- rubyforge_project: middleman-cloudfront
169
- rubygems_version: 2.4.5
170
- signing_key:
140
+ rubygems_version: 3.4.6
141
+ signing_key:
171
142
  specification_version: 4
172
143
  summary: Invalidate CloudFront cache after deployment to S3
173
- test_files:
174
- - features/support/env.rb
175
- has_rdoc:
144
+ test_files: []
data/.travis.yml DELETED
@@ -1,7 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 1.9
4
- - 2.0
5
- - 2.1
6
- - 2.2
7
- - jruby
@@ -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