capistrano-template 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +22 -0
  5. data/.rubocop_todo.yml +8 -0
  6. data/.travis.yml +4 -0
  7. data/.versions.conf +4 -0
  8. data/CHANGELOG.md +0 -0
  9. data/Gemfile +9 -0
  10. data/LICENSE.txt +22 -0
  11. data/README.md +66 -0
  12. data/Rakefile +16 -0
  13. data/capistrano-template.gemspec +35 -0
  14. data/lib/capistrano/capistrano_plugin_template.rb +5 -0
  15. data/lib/capistrano/template.rb +11 -0
  16. data/lib/capistrano/template/helpers/dsl.rb +61 -0
  17. data/lib/capistrano/template/helpers/paths_lookup.rb +33 -0
  18. data/lib/capistrano/template/helpers/renderer.rb +30 -0
  19. data/lib/capistrano/template/helpers/template_digester.rb +19 -0
  20. data/lib/capistrano/template/helpers/uploader.rb +94 -0
  21. data/lib/capistrano/template/tasks/template_defaults.cap +13 -0
  22. data/lib/capistrano/template/version.rb +5 -0
  23. data/spec/integration/capistrano/template/helpers/dsl_spec.rb +117 -0
  24. data/spec/integration/capistrano/template/helpers/paths_lookup_spec.rb +46 -0
  25. data/spec/integration/capistrano/template/helpers/uploader_spec.rb +100 -0
  26. data/spec/spec_helper.rb +42 -0
  27. data/spec/unit/capistrano/template/helpers/dsl_spec.rb +60 -0
  28. data/spec/unit/capistrano/template/helpers/paths_lookup_spec.rb +54 -0
  29. data/spec/unit/capistrano/template/helpers/renderer_spec.rb +43 -0
  30. data/spec/unit/capistrano/template/helpers/template_digester_spec.rb +36 -0
  31. data/spec/unit/capistrano/template/helpers/uploader_spec.rb +103 -0
  32. data/spec/unit/capistrano/template_spec.rb +7 -0
  33. metadata +186 -0
@@ -0,0 +1,13 @@
1
+ namespace :load do
2
+ task :defaults do
3
+ set :templating_digster, ->{ ->(data){ Digest::MD5.hexdigest(data)} }
4
+ set :templating_digest_cmd, %Q{test "Z$(openssl md5 %<path>s| sed "s/^.*= *//")" = "Z%<digest>s" } # alternative %Q{echo "%<digest>s %<path>s" | md5sum -c --status } should return true when the file content is the same
5
+ set :templating_mode_test_cmd, %Q{ [ "Z$(printf "%%.4o" 0$(stat -c "%%a" %<path>s 2>/dev/null || stat -f "%%A" %<path>s))" != "Z%<mode>s" ] } # mac uses different mode formatter
6
+ set :templating_paths , ->{ ["config/deploy/templates/#{fetch(:stage)}/%<host>s",
7
+ "config/deploy/templates/#{fetch(:stage)}",
8
+ "config/deploy/templates/shared/%<host>s",
9
+ "config/deploy/templates/shared"].map {|partial_path| (partial_path)} }
10
+ end
11
+ end
12
+
13
+
@@ -0,0 +1,5 @@
1
+ module Capistrano
2
+ module Template
3
+ VERSION = '0.0.1'
4
+ end
5
+ end
@@ -0,0 +1,117 @@
1
+ require 'spec_helper'
2
+
3
+ module Capistrano
4
+ module Template
5
+ module Helpers
6
+ module Integration # prodect from other dummy classes
7
+ module DSLSpec
8
+ class Dummy
9
+ include DSL
10
+ attr_accessor :data, :release_path
11
+
12
+ def initialize
13
+ self.data = {
14
+ templating_digster: ->(data) { Digest::MD5.hexdigest(data) },
15
+ templating_digest_cmd: %Q(echo "%<digest>s %<path>s" | md5sum -c --status ),
16
+ templating_mode_test_cmd: %Q{ [ "Z$(printf "%%.4o" 0$(stat -c "%%a" %<path>s 2>/dev/null || stat -f "%%A" %<path>s))" != "Z%<mode>s" ] }
17
+ }
18
+ end
19
+
20
+ # custom methods
21
+ def var1
22
+ 'my'
23
+ end
24
+
25
+ def var2
26
+ 'content'
27
+ end
28
+
29
+ # capistrano method
30
+ def fetch(*args)
31
+ data.fetch(*args)
32
+ end
33
+
34
+ # sshkit metthods
35
+
36
+ def host
37
+ 'localhost'
38
+ end
39
+
40
+ def test(*args)
41
+ execute(*args)
42
+ end
43
+
44
+ def execute(*args)
45
+ system(*args)
46
+ end
47
+
48
+ def upload!(io, filename)
49
+ File.write(filename, io.read, mode: 'w')
50
+ end
51
+
52
+ def capture(cmd)
53
+ `#{cmd}`
54
+ end
55
+
56
+ def info(*)
57
+ end
58
+
59
+ def error(*)
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ describe DSL do
66
+ subject do
67
+ Integration::DSLSpec::Dummy.new.tap do |d|
68
+ d.data[:templating_paths] = [tmp_folder]
69
+ d.release_path = tmp_folder
70
+ end
71
+ end
72
+
73
+ let(:template_name) { 'my_template.erb' }
74
+
75
+ let(:tmp_folder) { File.join(__dir__, '..', '..', '..', 'tmp') }
76
+
77
+ let(:template_content) { '<%=var1%> -- <%=var2%>' }
78
+ let(:expected_content) { 'my -- content' }
79
+
80
+ let(:template_name) { 'my_template.erb' }
81
+ let(:template_fullname) { File.join(tmp_folder, template_name) }
82
+ let(:remote_filename) { File.join(tmp_folder, 'my_template') }
83
+
84
+ let(:digest_algo) { ->(data) { Digest::MD5.hexdigest(data) } }
85
+ let(:digest_cmd) { %Q{test "Z$(openssl md5 %<path>s| sed "s/^.*= *//")" = "Z%<digest>s" } }
86
+
87
+ let(:mode_test_cmd) do
88
+ %Q{ [ "Z$(printf "%%.4o" 0$(stat -c "%%a" %<path>s 2>/dev/null || stat -f "%%A" %<path>s))" != "Z%<mode>s" ] }
89
+ end
90
+
91
+ before :each do
92
+ Dir.mkdir(tmp_folder) unless Dir.exist? tmp_folder
93
+ File.write(template_fullname, template_content, mode: 'w')
94
+ end
95
+
96
+ after :each do
97
+ [
98
+ template_fullname,
99
+ remote_filename
100
+ ].each do |f|
101
+ system('rm', '-f', f) if File.exist? f
102
+ end
103
+ end
104
+
105
+ describe '#template' do
106
+
107
+ it 'create the result file' do
108
+ subject.template(template_name)
109
+
110
+ expect(File.read(remote_filename)).to eq(expected_content)
111
+ end
112
+
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ module Capistrano
4
+ module Template
5
+ module Helpers
6
+ describe PathsLookup do
7
+ subject do
8
+ PathsLookup.new(lookup_paths, context)
9
+ end
10
+
11
+ let(:tmp_folder) { File.join(__dir__, '..', '..', '..', 'tmp') }
12
+
13
+ let(:lookup_paths) { ["#{tmp_folder}/%<host>s", "#{tmp_folder}"] }
14
+ let(:context) { OpenStruct.new(host: 'localhost') }
15
+
16
+ let(:template_content) { '<%=var1%> -- <%=var2%>' }
17
+ let(:template_name) { 'my_template.erb' }
18
+ let(:template_fullname) { File.join(tmp_folder, template_name) }
19
+
20
+ before :each do
21
+ Dir.mkdir(tmp_folder) unless Dir.exist? tmp_folder
22
+ File.write(template_fullname, template_content, mode: 'w')
23
+ end
24
+
25
+ after :each do
26
+ if File.exist? template_fullname
27
+ system('rm', '-f', File.join(tmp_folder, template_fullname))
28
+ end
29
+ end
30
+
31
+ describe '#template_exists?' do
32
+
33
+ it 'returns true when a template file exists' do
34
+ expect(subject.template_exists?(template_name)).to be_truthy
35
+ end
36
+
37
+ it 'returns false when a template does not file exists' do
38
+ expect(subject.template_exists?("#{template_name}.not_exists")).to be_falsy
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,100 @@
1
+ require 'spec_helper'
2
+
3
+ module Capistrano
4
+ module Template
5
+ module Helpers
6
+ describe Uploader do
7
+
8
+ before :each do
9
+ Dir.mkdir(tmp_folder) unless Dir.exist? tmp_folder
10
+ end
11
+
12
+ after :each do
13
+ system('rm', '-f', remote_filename) if File.exist? remote_filename
14
+ end
15
+
16
+ subject do
17
+ Uploader.new(
18
+ remote_filename,
19
+ context,
20
+ mode: 0640,
21
+ mode_test_cmd: mode_test_cmd,
22
+ digest: digest,
23
+ digest_cmd: digest_cmd,
24
+ io: as_io
25
+ )
26
+ end
27
+
28
+ let(:context) do
29
+ Struct.new(:host).new.tap do |cont|
30
+ cont.host = 'localhost'
31
+
32
+ allow(cont).to receive(:info)
33
+ allow(cont).to receive(:error)
34
+
35
+ def cont.test(*args)
36
+ system(*args)
37
+ end
38
+
39
+ def cont.execute(*args)
40
+ system(*args)
41
+ end
42
+
43
+ def cont.upload!(io, filename)
44
+ File.write(filename, io.read, mode: 'w')
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ let(:tmp_folder) { File.join(__dir__, '..', '..', '..', 'tmp') }
52
+
53
+ let(:rendered_template_content) { 'my -- content' }
54
+ let(:as_io) { StringIO.new(rendered_template_content) }
55
+
56
+ let(:remote_filename) { File.join(tmp_folder, 'my_template') }
57
+
58
+ let(:digest) { Digest::MD5.hexdigest(rendered_template_content) }
59
+ let(:digest_cmd) { %Q{test "Z$(openssl md5 %<path>s| sed "s/^.*= *//")" = "Z%<digest>s" } }
60
+
61
+ let(:mode_test_cmd) do
62
+ %Q{ [ "Z$(printf "%%.4o" 0$(stat -c "%%a" %<path>s 2>/dev/null || stat -f "%%A" %<path>s))" != "Z%<mode>s" ] }
63
+ end
64
+
65
+ describe '#call' do
66
+
67
+ it 'uploads a template when content has changed' do
68
+ subject.call
69
+ expect(File.exist?(remote_filename)).to be_truthy
70
+ end
71
+
72
+ it 'does not upload a template when content is equal' do
73
+ File.write(remote_filename, rendered_template_content, mode: 'w')
74
+
75
+ expect(context).not_to receive(:upload!)
76
+ subject.call
77
+ end
78
+
79
+ it 'evals the erb' do
80
+ subject.call
81
+ expect(File.read(remote_filename)).to eq(rendered_template_content)
82
+ end
83
+
84
+ it 'sets permissions' do
85
+ File.write(remote_filename, rendered_template_content, mode: 'w')
86
+ File.chmod(0400, remote_filename)
87
+
88
+ subject.call
89
+
90
+ mode = File.stat(remote_filename).mode & 0xFFF
91
+
92
+ expect(mode).to eq(0640)
93
+ end
94
+
95
+ end
96
+
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,42 @@
1
+ require 'bundler/setup'
2
+ Bundler.require(:development)
3
+
4
+ require 'coveralls'
5
+ Coveralls.wear! unless ENV['SIMPLE_COVERAGE']
6
+
7
+ begin
8
+ if ENV['SIMPLE_COVERAGE']
9
+ require 'simplecov'
10
+ SimpleCov.start do
11
+ add_group 'Lib', 'lib'
12
+
13
+ add_filter '/spec/'
14
+ end
15
+ end
16
+ rescue LoadError
17
+ warn '=' * 80
18
+ warn 'simplecov not installed. No coverage report'
19
+ warn '=' * 80
20
+ end
21
+
22
+ require 'capistrano/template'
23
+
24
+ Dir[File.join(File.expand_path(__dir__), 'support/**/*.rb')].each { |f| require f }
25
+
26
+ # This file was generated by the `rspec --init` command. Conventionally, all
27
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
28
+ # Require this file using `require "spec_helper"` to ensure that it is only
29
+ # loaded once.
30
+ #
31
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
32
+ RSpec.configure do |config|
33
+ config.treat_symbols_as_metadata_keys_with_true_values = true
34
+ config.run_all_when_everything_filtered = true
35
+ config.filter_run :focus
36
+
37
+ # Run specs in random order to surface order dependencies. If you find an
38
+ # order dependency and want to debug it, you can fix the order by providing
39
+ # the seed, which is printed after each run.
40
+ # --seed 1234
41
+ config.order = 'random'
42
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ module Capistrano
4
+ module Template
5
+ module Helpers
6
+ module Unit # prodect from other dummy classes
7
+ module DSLSpec
8
+ class Dummy
9
+ include DSL
10
+ attr_accessor :data, :file_exists
11
+
12
+ def initialize
13
+ self.file_exists = true
14
+ self.data = {
15
+ templating_paths: ['/tmp'],
16
+ }
17
+ end
18
+
19
+ def host
20
+ 'localhost'
21
+ end
22
+
23
+ def release_path
24
+ '/var/www/app/releases/20140510'
25
+ end
26
+
27
+ def fetch(*args)
28
+ data.fetch(*args)
29
+ end
30
+
31
+ def _paths_factory
32
+ lambda do |*args|
33
+ PathsLookup.new(*args).tap do |pl|
34
+ def pl.existence_check(*)
35
+ file_exists
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ describe DSL do
45
+ subject do
46
+ Unit::DSLSpec::Dummy.new
47
+ end
48
+
49
+ let(:template_name) { 'my_template.erb' }
50
+
51
+ describe '#template' do
52
+ it 'raises an exception when template does not exists' do
53
+ subject.file_exists = false
54
+ expect { subject.template(template_name) }.to raise_error(ArgumentError, /template #{template_name} not found Paths/)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ module Capistrano
4
+ module Template
5
+ module Helpers
6
+ describe PathsLookup do
7
+ subject do
8
+ PathsLookup.new(lookup_paths, context)
9
+ end
10
+
11
+ let(:lookup_paths) { ['path1/%<host>s', 'path2'] }
12
+ let(:context) { OpenStruct.new(host: 'localhost') }
13
+ let(:template_name) { 'my_template' }
14
+
15
+ describe '#template_exists?' do
16
+
17
+ it 'returns true when a template file exists' do
18
+ subject.stub(existence_check: true)
19
+ expect(subject.template_exists?(template_name)).to be_truthy
20
+ end
21
+
22
+ it 'returns false when a template does not file exists' do
23
+ subject.stub(existence_check: false)
24
+ expect(subject.template_exists?(template_name)).to be_falsy
25
+ end
26
+
27
+ it 'checks for every possible path existence' do
28
+ expect(subject).to receive(:existence_check).exactly(lookup_paths.count * 2).times
29
+ subject.template_exists?(template_name)
30
+ end
31
+
32
+ it 'stops search for first hit' do
33
+ expect(subject).to receive(:existence_check).exactly(2).times.and_return(false, true)
34
+ subject.template_exists?(template_name)
35
+ end
36
+
37
+ end
38
+
39
+ describe '#template_file' do
40
+ it 'returns the first found filename' do
41
+ allow(subject).to receive(:existence_check).and_return(false, false, true)
42
+ expect(subject.template_file(template_name)).to eq('path2/my_template.erb')
43
+ end
44
+
45
+ it 'expends the host' do
46
+ allow(subject).to receive(:existence_check).and_return(true)
47
+ expect(subject.template_file(template_name)).to eq('path1/localhost/my_template.erb')
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
54
+ end