middleman-cloudfront 0.2.1 → 0.4.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: 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