revision_plate 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0b7a681b78b1521300089eec60a31ddf500e2497
4
+ data.tar.gz: 00b444374ae5a0364fcee8fa9b9dfada64bc30a0
5
+ SHA512:
6
+ metadata.gz: 2d0a4f7068344548015eceb82d7c8273683be979cb85726f8445514a3a2e62ba9c0548737d4128b75ae57d378b4febc82a3e33bf764dbddef0b21beba3b2be4d
7
+ data.tar.gz: b0b47299856a9c9373a3c468c7e65d799050e2d88622fb5e59cb57c8b01059429fd485b5dc21fb5687756dc91cef43a0d6687cccc61d98b3eb49c0fc01f474a0
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.travis.yml ADDED
@@ -0,0 +1,21 @@
1
+ language: ruby
2
+ cache: bundler
3
+ sudo: false
4
+
5
+ rvm:
6
+ - 2.0.0
7
+ - 2.1
8
+ - 2.2
9
+ - ruby-head
10
+
11
+ matrix:
12
+ fast_finish: true
13
+ allow_failures:
14
+ - rvm: "ruby-head"
15
+
16
+ before_install:
17
+ - gem i bundler -v 1.8.4
18
+
19
+ script:
20
+ - bundle exec rake
21
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in revision_plate.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Shota Fukumori (sora_h)
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # RevisionPlate
2
+
3
+ Rack application and middleware that serves endpoint returns application's `REVISION`.
4
+
5
+ ## Detail
6
+
7
+ The endpoint returns content of `REVISION`
8
+
9
+ Content of the endpoint wouldn't be changed even if `REVISION` file has changed. But it'll return 404 when it has removed.
10
+
11
+ This can be used for health check + remove from service by hand.
12
+
13
+ This gem is used in [Cookpad](https://info.cookpad.com/).
14
+ And seems several companies runs similar thing (e.g. [GitHub](https://github.com/blog/609-tracking-deploys-with-compare-view)).
15
+
16
+ ## Usage
17
+
18
+ ### typical Rails app
19
+
20
+ ``` ruby
21
+ # Gemfile
22
+ gem 'revision_plate', require: 'revision_plate/rails'
23
+ ```
24
+
25
+ then your Rails application will handle `/site/sha`.
26
+
27
+ ### rack application
28
+
29
+ ``` ruby
30
+ # Gemfile
31
+ gem 'revision_plate'
32
+
33
+ # config.ru (middleware)
34
+ use RevisionPlate::Middleware, '/site/sha', "#{__dir__}/REVISION"
35
+
36
+ # config.ru (mount)
37
+ map '/site/sha' do
38
+ run RevisionPlate::App.new("#{__dir__}/REVISION")
39
+ end
40
+ ```
41
+
42
+ ## Test
43
+
44
+ ```
45
+ $ echo 'deadbeef' > REVISION
46
+ $ (... start your app ...)
47
+ $ curl localhost:3000/site/sha
48
+ deadbeef
49
+ $ rm REVISION
50
+ $ curl localhost:3000/site/sha
51
+ REVISION_FILE_REMOVED
52
+ ```
53
+
54
+ ## Advanced
55
+
56
+ ### I want to customize (Rails app)
57
+
58
+ remove `require: 'revision_plate/rails'` from Gemfile, then initialize `RevisionPlate::App` on routes:
59
+
60
+ ```
61
+ # routes.rb
62
+ get '/site/sha' => RevisionPlate::App.new
63
+ get '/site/sha' => RevisionPlate::App.new("/path/to/my/favorite/REVISION")
64
+ ```
65
+
66
+ ## Development
67
+
68
+ ### Testing
69
+
70
+ ```
71
+ $ rake test
72
+ ```
73
+
74
+ ## License
75
+
76
+ MIT License
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ ENV['gem_push']='false'
2
+ require "bundler/gem_tasks"
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new(:test) do |t|
6
+ t.test_files = FileList['test/*_test.rb']
7
+ t.warning = true
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,3 @@
1
+ require "revision_plate/version"
2
+ require "revision_plate/app"
3
+ require 'revision_plate/middleware'
@@ -0,0 +1,43 @@
1
+ require 'pathname'
2
+
3
+ module RevisionPlate
4
+ class App
5
+ def initialize(file = nil, path: nil)
6
+ @file = file
7
+ @path = path
8
+ if @file
9
+ unless @file.kind_of?(Pathname)
10
+ @file = Pathname.new(@file)
11
+ end
12
+ else
13
+ if defined? Rails
14
+ @file = Rails.root.join('REVISION')
15
+ else
16
+ raise ArgumentError, "couldn't locate REVISION file"
17
+ end
18
+ end
19
+
20
+ if @file.exist?
21
+ @revision = @file.read.chomp
22
+ else
23
+ @revision = nil
24
+ end
25
+ end
26
+
27
+ def call(env)
28
+ unless env['REQUEST_METHOD'] == 'GET' && (@path ? env['PATH_INFO'] == @path : true)
29
+ return [404, {'Content-Type' => 'text/plain'}, []]
30
+ end
31
+
32
+ if @revision
33
+ if @file.exist?
34
+ [200, {'Content-Type' => 'text/plain'}, [@revision, ?\n]]
35
+ else
36
+ [404, {'Content-Type' => 'text/plain'}, ["REVISION_FILE_REMOVED\n"]]
37
+ end
38
+ else
39
+ [404, {'Content-Type' => 'text/plain'}, ["REVISION_FILE_NOT_FOUND\n"]]
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,19 @@
1
+ require 'revision_plate/app'
2
+
3
+ module RevisionPlate
4
+ class Middleware
5
+ def initialize(app, path, file = nil, options={})
6
+ @path = path
7
+ @app = app
8
+ @revision_app = App.new(file, **options)
9
+ end
10
+
11
+ def call(env)
12
+ if env['PATH_INFO'] == @path && env['REQUEST_METHOD'] == 'GET'
13
+ @revision_app.call(env)
14
+ else
15
+ @app.call(env)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ require 'revision_plate'
2
+
3
+ if defined?(Rails)
4
+ module RevisionPlate
5
+ class Railtie < Rails::Railtie
6
+ initializer "revision_plate.rails_middleware" do |app|
7
+ app.middleware.use RevisionPlate::Middleware, '/site/sha'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module RevisionPlate
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'revision_plate/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "revision_plate"
8
+ spec.version = RevisionPlate::VERSION
9
+ spec.authors = ["Shota Fukumori (sora_h)"]
10
+ spec.email = ["sorah@cookpad.com"]
11
+ spec.summary = %q{Rack middleware and application to show deployed application's revision (commit)}
12
+ spec.description = nil
13
+ spec.homepage = "https://github.com/sorah/revision_plate"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", ">= 1.7.8"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "minitest", "~> 5.5.1"
24
+ spec.add_development_dependency "rack-test", "~> 0.6.3"
25
+
26
+ spec.add_dependency "rack"
27
+ end
data/test/app_test.rb ADDED
@@ -0,0 +1,121 @@
1
+ require_relative './test_helper'
2
+ require 'tmpdir'
3
+
4
+ require 'revision_plate/app'
5
+
6
+ class AppTest < Minitest::Spec
7
+ include Rack::Test::Methods
8
+ alias_method :response, :last_response
9
+
10
+ describe 'app' do
11
+ let(:app) { RevisionPlate::App.new(file) }
12
+ let(:tmpdir) { Dir.mktmpdir('revision_plate_app_test') }
13
+ let(:file) { Pathname.new(tmpdir).join('REVISION') }
14
+
15
+ before do
16
+ File.write file.to_s, "a"
17
+ end
18
+
19
+ describe 'with default' do
20
+ let(:app) { RevisionPlate::App.new }
21
+
22
+ describe 'with Rails' do
23
+ before do
24
+ RevisionPlate.const_set :Rails, Class.new {
25
+ def self.root
26
+ @root
27
+ end
28
+ def self.root=(o)
29
+ @root = Pathname.new(o)
30
+ end
31
+ }.tap { |klass|
32
+ klass.root = tmpdir
33
+ }
34
+ end
35
+
36
+ after do
37
+ RevisionPlate.send :remove_const, :Rails
38
+ end
39
+
40
+ it 'returns RAILS_ROOT/REVISION' do
41
+ get '/'
42
+ assert_equal 200, response.status
43
+ assert_equal "a\n", response.body
44
+ end
45
+ end
46
+
47
+ describe 'without Rails' do
48
+ it "raises error" do
49
+ assert_raises(ArgumentError) { app }
50
+ end
51
+ end
52
+ end
53
+
54
+ describe 'with path' do
55
+ let(:app) { RevisionPlate::App.new(file, path: '/site/sha') }
56
+
57
+ it "won't respond to request to invalid path" do
58
+ get '/site/sha'
59
+ assert_equal 200, response.status
60
+ assert_equal "a\n", response.body
61
+
62
+ post '/site/sha'
63
+ assert_equal 404, response.status
64
+
65
+ get '/site/sha/a'
66
+ assert_equal 404, response.status
67
+
68
+ get '/'
69
+ assert_equal 404, response.status
70
+ end
71
+ end
72
+
73
+ it 'returns specified file' do
74
+ get '/'
75
+ assert_equal 200, response.status
76
+ assert_equal "a\n", response.body
77
+ end
78
+
79
+ it 'returns same revision even if updated' do
80
+ app # ensure to instantiate
81
+ File.write file.to_s, "b\n"
82
+
83
+ get '/'
84
+ assert_equal 200, response.status
85
+ assert_equal "a\n", response.body
86
+ end
87
+
88
+ it 'returns 404 for POST request' do
89
+ post '/'
90
+ assert_equal 404, response.status
91
+ end
92
+
93
+ it 'returns 404 if removed' do
94
+ app # ensure to instantiate
95
+ file.unlink
96
+
97
+ get '/'
98
+ assert_equal 404, response.status
99
+ assert_equal "REVISION_FILE_REMOVED\n", response.body
100
+ end
101
+
102
+ it 'returns 404 if not exists' do
103
+ file.unlink
104
+ app # instantiate
105
+
106
+ get '/'
107
+ assert_equal 404, response.status
108
+ assert_equal "REVISION_FILE_NOT_FOUND\n", response.body
109
+ end
110
+
111
+ it 'returns 404 if created but it was not existed' do
112
+ file.unlink
113
+ app # instantiate
114
+ File.write file.to_s, "b\n"
115
+
116
+ get '/'
117
+ assert_equal 404, response.status
118
+ assert_equal "REVISION_FILE_NOT_FOUND\n", response.body
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,75 @@
1
+ require_relative './test_helper'
2
+ require 'tempfile'
3
+
4
+ require 'revision_plate/middleware'
5
+
6
+ module RevisionPlate
7
+ AppOriginal = App
8
+ end
9
+
10
+ class AppTest < Minitest::Spec
11
+ include Rack::Test::Methods
12
+ alias_method :response, :last_response
13
+
14
+ describe 'middleware' do
15
+ let(:tempfile) { Tempfile.new('revision_plate-middielware-test') }
16
+
17
+ let(:nextapp) { -> (env) { [200, {'Content-Type' => 'text/plain'}, ['hi']] } }
18
+ let(:mockapp) do
19
+ Class.new do
20
+ def self.instances
21
+ @instances ||= []
22
+ end
23
+
24
+ def initialize(file, **options)
25
+ @file = file
26
+ @options = options
27
+ self.class.instances << self
28
+ end
29
+
30
+ attr_reader :file, :options
31
+
32
+ def call(env)
33
+ [200, {'Content-Type' => 'text/plain'}, "deadbeef"]
34
+ end
35
+ end
36
+ end
37
+
38
+ let(:app) { RevisionPlate::Middleware.new(nextapp, '/site/sha', tempfile.path, opt: :option) }
39
+
40
+ before do
41
+ RevisionPlate.send(:remove_const, :App)
42
+ RevisionPlate.const_set(:App, mockapp)
43
+ end
44
+
45
+ after do
46
+ RevisionPlate.send(:remove_const, :App)
47
+ RevisionPlate.const_set(:App, RevisionPlate::AppOriginal)
48
+ end
49
+
50
+ it 'instantiates App with proper argument' do
51
+ app
52
+
53
+ assert_equal 1, mockapp.instances.size
54
+ assert_equal({opt: :option}, mockapp.instances.first.options)
55
+ assert_equal tempfile.path, mockapp.instances.first.file
56
+ end
57
+
58
+ it 'pass-through requests to nextapp' do
59
+ get '/'
60
+ assert_equal response.body, 'hi'
61
+ post '/'
62
+ assert_equal response.body, 'hi'
63
+ get '/site'
64
+ assert_equal response.body, 'hi'
65
+ post '/site/sha'
66
+ assert_equal response.body, 'hi'
67
+ end
68
+
69
+ it 'pass requests to RevisionPlate::App on specific path' do
70
+ get '/site/sha'
71
+ assert_equal response.body, 'deadbeef'
72
+ end
73
+ end
74
+ end
75
+
@@ -0,0 +1,4 @@
1
+ require 'minitest'
2
+ require 'minitest/spec'
3
+ require 'rack/test'
4
+ require "minitest/autorun"
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: revision_plate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Shota Fukumori (sora_h)
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-03-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.7.8
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.7.8
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 5.5.1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 5.5.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: rack-test
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.6.3
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.6.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: rack
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: ''
84
+ email:
85
+ - sorah@cookpad.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".travis.yml"
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - lib/revision_plate.rb
97
+ - lib/revision_plate/app.rb
98
+ - lib/revision_plate/middleware.rb
99
+ - lib/revision_plate/rails.rb
100
+ - lib/revision_plate/version.rb
101
+ - revision_plate.gemspec
102
+ - test/app_test.rb
103
+ - test/middleware_test.rb
104
+ - test/test_helper.rb
105
+ homepage: https://github.com/sorah/revision_plate
106
+ licenses:
107
+ - MIT
108
+ metadata: {}
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ requirements: []
124
+ rubyforge_project:
125
+ rubygems_version: 2.4.5
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: Rack middleware and application to show deployed application's revision (commit)
129
+ test_files:
130
+ - test/app_test.rb
131
+ - test/middleware_test.rb
132
+ - test/test_helper.rb