capistrano-template 0.0.1
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 +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.rubocop.yml +22 -0
- data/.rubocop_todo.yml +8 -0
- data/.travis.yml +4 -0
- data/.versions.conf +4 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +66 -0
- data/Rakefile +16 -0
- data/capistrano-template.gemspec +35 -0
- data/lib/capistrano/capistrano_plugin_template.rb +5 -0
- data/lib/capistrano/template.rb +11 -0
- data/lib/capistrano/template/helpers/dsl.rb +61 -0
- data/lib/capistrano/template/helpers/paths_lookup.rb +33 -0
- data/lib/capistrano/template/helpers/renderer.rb +30 -0
- data/lib/capistrano/template/helpers/template_digester.rb +19 -0
- data/lib/capistrano/template/helpers/uploader.rb +94 -0
- data/lib/capistrano/template/tasks/template_defaults.cap +13 -0
- data/lib/capistrano/template/version.rb +5 -0
- data/spec/integration/capistrano/template/helpers/dsl_spec.rb +117 -0
- data/spec/integration/capistrano/template/helpers/paths_lookup_spec.rb +46 -0
- data/spec/integration/capistrano/template/helpers/uploader_spec.rb +100 -0
- data/spec/spec_helper.rb +42 -0
- data/spec/unit/capistrano/template/helpers/dsl_spec.rb +60 -0
- data/spec/unit/capistrano/template/helpers/paths_lookup_spec.rb +54 -0
- data/spec/unit/capistrano/template/helpers/renderer_spec.rb +43 -0
- data/spec/unit/capistrano/template/helpers/template_digester_spec.rb +36 -0
- data/spec/unit/capistrano/template/helpers/uploader_spec.rb +103 -0
- data/spec/unit/capistrano/template_spec.rb +7 -0
- 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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|