statistrano 1.2.3 → 1.3.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 +4 -4
- data/changelog.md +10 -0
- data/lib/statistrano/config.rb +39 -8
- data/lib/statistrano/config/configurable.rb +13 -4
- data/lib/statistrano/deployment/strategy/base.rb +4 -1
- data/lib/statistrano/deployment/strategy/branches.rb +4 -0
- data/lib/statistrano/deployment/strategy/releases.rb +9 -0
- data/lib/statistrano/remote.rb +11 -3
- data/lib/statistrano/shell.rb +1 -1
- data/lib/statistrano/version.rb +1 -1
- data/spec/lib/statistrano/config/configurable_spec.rb +14 -1
- data/spec/lib/statistrano/config_spec.rb +57 -8
- data/spec/lib/statistrano/deployment/strategy/base_spec.rb +16 -0
- data/spec/lib/statistrano/deployment/strategy/branches_spec.rb +13 -1
- data/spec/lib/statistrano/deployment/strategy/releases_spec.rb +34 -2
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 687953197199b61ff409d6d2ca46af228ddd046b
|
4
|
+
data.tar.gz: 8ac0ee0ec0b9637728ca792c6e2b6301bd963acc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef1939faf699e13754e73d363725f4f8fb9ad448c022e88f4a11c98cb9041f00414f960422426342da392309774385fac921691e19027054e07a38119ae0ba89
|
7
|
+
data.tar.gz: bbd659c40f1589b2d7ff3c497f73052c1dc452158309df8b7e73385e8d754a7b0e789ceaa152c1449d8f166ac99e53474b8f6c85e2bdb4728f1a92b9a6a50e45
|
data/changelog.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
# 1.3.0
|
2
|
+
- feat(config validation) setup validators for some optons that could be descructive if not set:
|
3
|
+
```
|
4
|
+
Base: remote_dir
|
5
|
+
Branches: public_dir
|
6
|
+
Releases: release_count, public_dir, releases_dir
|
7
|
+
```
|
8
|
+
- refactor(config) change the internal method signature of Config to use keyword arguments `options`, and `tasks`
|
9
|
+
- add a `run_local` method to Remote to wrap `Shell.run_local` to display commands when in verbose mode.
|
10
|
+
|
1
11
|
# 1.2.3
|
2
12
|
- fix bug in manifest creation/saving from changes in ruby 2.2.x
|
3
13
|
|
data/lib/statistrano/config.rb
CHANGED
@@ -5,23 +5,42 @@ module Statistrano
|
|
5
5
|
class Config
|
6
6
|
include RakeTaskWithContextCreation
|
7
7
|
|
8
|
-
attr_reader :options
|
9
|
-
|
8
|
+
attr_reader :options,
|
9
|
+
:tasks,
|
10
|
+
:validators
|
10
11
|
|
11
12
|
# initalize with the potential for seed options
|
12
13
|
# this is required so that when config'd classes
|
13
14
|
# are extended we can pass that configuration along
|
14
|
-
def initialize options
|
15
|
-
@options
|
16
|
-
@tasks
|
15
|
+
def initialize options: nil, tasks: nil, validators: nil
|
16
|
+
@options = options.nil? ? {} : options.clone
|
17
|
+
@tasks = tasks.nil? ? {} : tasks.clone
|
18
|
+
@validators = validators.nil? ? {} : validators.clone
|
17
19
|
|
18
20
|
@options.each do |key,val|
|
19
21
|
define_option_accessor key.to_sym
|
20
22
|
end
|
23
|
+
|
24
|
+
@validators.each do |key,val|
|
25
|
+
define_validator key.to_sym
|
26
|
+
end
|
21
27
|
end
|
22
28
|
|
23
29
|
private
|
24
30
|
|
31
|
+
def define_validator name
|
32
|
+
define_singleton_method(:"validator_for_#{name}") do |proc, message|
|
33
|
+
@validators[name] = { validator: proc,
|
34
|
+
message: message }
|
35
|
+
end
|
36
|
+
|
37
|
+
define_singleton_method(:"validate_#{name}") do |arg|
|
38
|
+
if @validators.has_key?(name) && !@validators[name][:validator].call(arg)
|
39
|
+
raise ValidationError, (@validators[name][:message] || "configuration option for '#{name}' failed it's validation")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
25
44
|
def define_option_accessor name
|
26
45
|
define_singleton_method(name) do |*args, &block|
|
27
46
|
if block
|
@@ -34,13 +53,22 @@ module Statistrano
|
|
34
53
|
end
|
35
54
|
|
36
55
|
if args.length == 1
|
37
|
-
|
56
|
+
val = args[0]
|
57
|
+
|
58
|
+
if @validators.has_key?(name)
|
59
|
+
val = val.call if val.respond_to?(:call)
|
60
|
+
send "validate_#{name}", val
|
61
|
+
end
|
62
|
+
|
63
|
+
@options[name] = val
|
38
64
|
elsif args.empty?
|
39
65
|
if @options[name].respond_to? :fetch
|
40
|
-
@options[name].fetch( :call, -> { @options[name] } ).call
|
66
|
+
opt = @options[name].fetch( :call, -> { @options[name] } ).call
|
41
67
|
else
|
42
|
-
@options[name]
|
68
|
+
opt = @options[name]
|
43
69
|
end
|
70
|
+
|
71
|
+
opt
|
44
72
|
else
|
45
73
|
raise ArgumentError, "wrong number of arguments (#{args.length} for 0..1)"
|
46
74
|
end
|
@@ -48,5 +76,8 @@ module Statistrano
|
|
48
76
|
define_singleton_method("#{name}=") { |arg| @options[name] = arg }
|
49
77
|
end
|
50
78
|
|
79
|
+
class ValidationError < StandardError
|
80
|
+
end
|
81
|
+
|
51
82
|
end
|
52
83
|
end
|
@@ -6,7 +6,7 @@ module Statistrano
|
|
6
6
|
@_configuration ||= Config.new()
|
7
7
|
end
|
8
8
|
|
9
|
-
def option key, value=nil, proc=nil
|
9
|
+
def option key, value=nil, proc=nil, opts={}
|
10
10
|
if proc
|
11
11
|
configuration.options[key] = { call: proc }
|
12
12
|
else
|
@@ -14,6 +14,13 @@ module Statistrano
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
def validate key, proc, message=nil
|
18
|
+
configuration.validators[key] = { validator: proc }
|
19
|
+
if message
|
20
|
+
configuration.validators[key][:message] = message
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
17
24
|
def options *args
|
18
25
|
args.each { |a| option(a) }
|
19
26
|
end
|
@@ -32,8 +39,9 @@ module Statistrano
|
|
32
39
|
extending_obj.send :define_method,
|
33
40
|
:config,
|
34
41
|
-> {
|
35
|
-
@_config ||= Config.new( self.class.configuration.options,
|
36
|
-
self.class.configuration.tasks
|
42
|
+
@_config ||= Config.new( options: self.class.configuration.options,
|
43
|
+
tasks: self.class.configuration.tasks,
|
44
|
+
validators: self.class.configuration.validators )
|
37
45
|
}
|
38
46
|
end
|
39
47
|
|
@@ -45,9 +53,10 @@ module Statistrano
|
|
45
53
|
subclass.superclass.class_eval do
|
46
54
|
subclass.configuration.options.merge! subclass.superclass.configuration.options
|
47
55
|
subclass.configuration.tasks.merge! subclass.superclass.configuration.tasks
|
56
|
+
subclass.configuration.validators.merge! subclass.superclass.configuration.validators
|
48
57
|
end
|
49
58
|
end
|
50
59
|
|
51
60
|
end
|
52
61
|
end
|
53
|
-
end
|
62
|
+
end
|
@@ -29,6 +29,9 @@ module Statistrano
|
|
29
29
|
|
30
30
|
option :verbose, false
|
31
31
|
|
32
|
+
validate :remote_dir, lambda { |d| !d.empty? && d != '/' },
|
33
|
+
"'remote_dir' must not be empty or '/'"
|
34
|
+
|
32
35
|
task :deploy, :deploy, "Deploy to remote"
|
33
36
|
task :build, :invoke_build_task, "Run build task"
|
34
37
|
task :post_deploy, :invoke_post_deploy_task, "Run post deploy task"
|
@@ -84,7 +87,7 @@ module Statistrano
|
|
84
87
|
return @_remotes if @_remotes
|
85
88
|
|
86
89
|
@_remotes = config.options[:remotes].map do |remote_options|
|
87
|
-
Remote.new Config.new( config.options.dup.merge(remote_options) )
|
90
|
+
Remote.new Config.new( options: config.options.dup.merge(remote_options) )
|
88
91
|
end
|
89
92
|
@_remotes.push Remote.new(config) if @_remotes.empty?
|
90
93
|
|
@@ -16,11 +16,15 @@ module Statistrano
|
|
16
16
|
d.generate_index
|
17
17
|
}
|
18
18
|
|
19
|
+
validate :public_dir, lambda { |d| !d.to_s.empty? && d != '/' },
|
20
|
+
"'public_dir' can't be an empty string or '/'"
|
21
|
+
|
19
22
|
task :list, :list_releases, "List branches"
|
20
23
|
task :prune, :prune_releases, "Prune a branch"
|
21
24
|
task :generate_index, :generate_index, "Generate a branch index"
|
22
25
|
task :open, :open_url, "Open the current branch URL"
|
23
26
|
|
27
|
+
|
24
28
|
# output a list of the releases in manifest
|
25
29
|
# @return [Void]
|
26
30
|
def list_releases
|
@@ -35,6 +35,15 @@ module Statistrano
|
|
35
35
|
|
36
36
|
option :remotes, []
|
37
37
|
|
38
|
+
validate :release_dir, lambda { |d| !d.to_s.empty? && d != '/' },
|
39
|
+
"'release_dir' can't be an empty string or '/'"
|
40
|
+
|
41
|
+
validate :public_dir, lambda { |d| !d.to_s.empty? && d != '/' },
|
42
|
+
"'public_dir' can't be an empty string or '/'"
|
43
|
+
|
44
|
+
validate :release_count, lambda { |c| c.is_a?(Integer) && c > 0 },
|
45
|
+
"'release_count' must be an integer greater than 0"
|
46
|
+
|
38
47
|
task :deploy, :deploy, "Deploy to all remotes"
|
39
48
|
task :rollback, :rollback_release, "Rollback to the previous release"
|
40
49
|
task :prune, :prune_releases, "Prune releases to release count"
|
data/lib/statistrano/remote.rb
CHANGED
@@ -38,6 +38,14 @@ module Statistrano
|
|
38
38
|
session.run command
|
39
39
|
end
|
40
40
|
|
41
|
+
def run_local command
|
42
|
+
if config.verbose
|
43
|
+
Log.info :local, "running cmd: #{command}"
|
44
|
+
end
|
45
|
+
|
46
|
+
Shell.run_local command
|
47
|
+
end
|
48
|
+
|
41
49
|
def done
|
42
50
|
session.close_session
|
43
51
|
end
|
@@ -63,9 +71,9 @@ module Statistrano
|
|
63
71
|
Log.info "Syncing files from '#{local_path}' to '#{remote_path}' on #{config.hostname}"
|
64
72
|
|
65
73
|
time_before = Time.now
|
66
|
-
resp =
|
67
|
-
|
68
|
-
|
74
|
+
resp = run_local "rsync #{rsync_options} " +
|
75
|
+
"-e ssh #{local_path}/ " +
|
76
|
+
"#{host_connection}:#{remote_path}/"
|
69
77
|
time_after = Time.now
|
70
78
|
total_time = (time_after - time_before).round(2)
|
71
79
|
|
data/lib/statistrano/shell.rb
CHANGED
data/lib/statistrano/version.rb
CHANGED
@@ -11,6 +11,9 @@ describe Statistrano::Config::Configurable do
|
|
11
11
|
option :proc, -> { "hello" }
|
12
12
|
|
13
13
|
options :one, :two
|
14
|
+
|
15
|
+
validate :three, lambda { |arg| arg }
|
16
|
+
validate :four, lambda { |arg| arg }, "didn't pass"
|
14
17
|
end
|
15
18
|
end
|
16
19
|
|
@@ -47,6 +50,16 @@ describe Statistrano::Config::Configurable do
|
|
47
50
|
end
|
48
51
|
end
|
49
52
|
|
53
|
+
describe "#validate" do
|
54
|
+
it "adds valiator to configuration.validators" do
|
55
|
+
expect( subject.config.validators.keys ).to include :three
|
56
|
+
end
|
57
|
+
|
58
|
+
it "adds validator with description if given" do
|
59
|
+
expect( subject.config.validators[:four][:message] ).to eq "didn't pass"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
50
63
|
describe "option accessor" do
|
51
64
|
let(:config) { subject.config }
|
52
65
|
|
@@ -85,4 +98,4 @@ describe Statistrano::Config::Configurable do
|
|
85
98
|
end
|
86
99
|
|
87
100
|
|
88
|
-
end
|
101
|
+
end
|
@@ -3,31 +3,80 @@ require 'spec_helper'
|
|
3
3
|
describe Statistrano::Config do
|
4
4
|
|
5
5
|
describe "#initialize" do
|
6
|
-
it "defaults options &
|
6
|
+
it "defaults options, tasks, & validators to a blank hash" do
|
7
7
|
subject = described_class.new
|
8
8
|
|
9
9
|
expect( subject.options ).to eq({})
|
10
10
|
expect( subject.tasks ).to eq({})
|
11
|
+
expect( subject.validators ).to eq({})
|
11
12
|
end
|
12
13
|
|
13
14
|
it "defines an accessor for each given option" do
|
14
|
-
subject = described_class.new foo: 'bar'
|
15
|
+
subject = described_class.new options: { foo: 'bar' }
|
15
16
|
expect( subject.foo ).to eq 'bar'
|
16
17
|
end
|
17
18
|
|
18
|
-
it "uses given options
|
19
|
-
options
|
20
|
-
tasks
|
21
|
-
|
19
|
+
it "uses given options, tasks, and validators, but clones so the originals don't get modified" do
|
20
|
+
options = { foo: 'bar' }
|
21
|
+
tasks = { foo: 'bar' }
|
22
|
+
validators = { foo: { validator: lambda { |arg| arg } } }
|
22
23
|
|
23
|
-
subject
|
24
|
-
|
24
|
+
subject = described_class.new options: options, tasks: tasks, validators: validators
|
25
|
+
|
26
|
+
subject.foo = 'baz'
|
27
|
+
subject.tasks[:foo] = 'baz'
|
28
|
+
subject.validators[:foo] = { validator: lambda { |arg| 'baz' } }
|
25
29
|
|
26
30
|
expect( subject.foo ).to eq 'baz'
|
27
31
|
expect( options ).to eq foo: 'bar'
|
28
32
|
|
29
33
|
expect( subject.tasks ).to eq foo: 'baz'
|
30
34
|
expect( tasks ).to eq foo: 'bar'
|
35
|
+
|
36
|
+
expect( subject.validators[:foo][:validator].call('arg') ).to eq 'baz'
|
37
|
+
expect( validators[:foo][:validator].call('arg') ).to eq 'arg'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#accessor set" do
|
42
|
+
context "when a validator is set" do
|
43
|
+
let(:subject) do
|
44
|
+
subject = described_class.new options: { string: '', integer: '' },
|
45
|
+
validators: {
|
46
|
+
string: { validator: lambda { |i| !i.to_s.empty? } },
|
47
|
+
integer: { validator: lambda { |i| i.is_a?(Integer) } }
|
48
|
+
}
|
49
|
+
|
50
|
+
subject.validators[:integer][:message] = "not an integer"
|
51
|
+
|
52
|
+
subject
|
53
|
+
end
|
54
|
+
|
55
|
+
it "is peachy with a valid value" do
|
56
|
+
expect{
|
57
|
+
subject.validate_string 'a string'
|
58
|
+
}.not_to raise_error
|
59
|
+
end
|
60
|
+
|
61
|
+
it "raises when validating invalid value" do
|
62
|
+
expect{
|
63
|
+
subject.validate_string ''
|
64
|
+
}.to raise_error Statistrano::Config::ValidationError
|
65
|
+
end
|
66
|
+
|
67
|
+
it "raises while validating with given message" do
|
68
|
+
expect{
|
69
|
+
subject.validate_integer 'foobar'
|
70
|
+
}.to raise_error Statistrano::Config::ValidationError, 'not an integer'
|
71
|
+
end
|
72
|
+
|
73
|
+
it "raises when set to invalid value" do
|
74
|
+
expect{
|
75
|
+
subject.integer 'foo'
|
76
|
+
}.to raise_error Statistrano::Config::ValidationError
|
77
|
+
end
|
78
|
+
|
79
|
+
|
31
80
|
end
|
32
81
|
end
|
33
82
|
|
@@ -13,6 +13,22 @@ describe Statistrano::Deployment::Strategy::Base do
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
describe "config validation" do
|
17
|
+
it "doesn't allow remote_dir to be empty" do
|
18
|
+
subject = described_class.new "hello"
|
19
|
+
expect{
|
20
|
+
subject.config.remote_dir ""
|
21
|
+
}.to raise_error Statistrano::Config::ValidationError
|
22
|
+
end
|
23
|
+
|
24
|
+
it "doesn't allow you to set remote_dir as /" do
|
25
|
+
subject = described_class.new "hello"
|
26
|
+
expect{
|
27
|
+
subject.config.remote_dir "/"
|
28
|
+
}.to raise_error Statistrano::Config::ValidationError
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
16
32
|
describe "#deploy" do
|
17
33
|
|
18
34
|
before :each do
|
@@ -16,4 +16,16 @@ describe Statistrano::Deployment::Strategy::Branches do
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
|
19
|
+
it "validates public_dir not empty or '/'" do
|
20
|
+
deployment = described_class.new("name")
|
21
|
+
|
22
|
+
expect{
|
23
|
+
deployment.config.public_dir ""
|
24
|
+
}.to raise_error Statistrano::Config::ValidationError
|
25
|
+
|
26
|
+
expect{
|
27
|
+
deployment.config.public_dir "/"
|
28
|
+
}.to raise_error Statistrano::Config::ValidationError
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -6,6 +6,38 @@ describe Statistrano::Deployment::Strategy::Releases do
|
|
6
6
|
expect( Statistrano::Deployment::Strategy.find(:releases) ).to eq described_class
|
7
7
|
end
|
8
8
|
|
9
|
+
describe "config validation" do
|
10
|
+
let(:subject) { described_class.new('hello') }
|
11
|
+
|
12
|
+
it "prevents release_dir from being empty or '/'" do
|
13
|
+
expect{
|
14
|
+
subject.config.release_dir ''
|
15
|
+
}.to raise_error Statistrano::Config::ValidationError
|
16
|
+
|
17
|
+
expect{
|
18
|
+
subject.config.release_dir '/'
|
19
|
+
}.to raise_error Statistrano::Config::ValidationError
|
20
|
+
end
|
21
|
+
it "prevents public_dir from being empty or '/'" do
|
22
|
+
expect{
|
23
|
+
subject.config.public_dir ''
|
24
|
+
}.to raise_error Statistrano::Config::ValidationError
|
25
|
+
|
26
|
+
expect{
|
27
|
+
subject.config.public_dir '/'
|
28
|
+
}.to raise_error Statistrano::Config::ValidationError
|
29
|
+
end
|
30
|
+
it "requires release_count to be an integer > 0" do
|
31
|
+
expect{
|
32
|
+
subject.config.release_count 'four'
|
33
|
+
}.to raise_error Statistrano::Config::ValidationError
|
34
|
+
|
35
|
+
expect{
|
36
|
+
subject.config.release_count 0
|
37
|
+
}.to raise_error Statistrano::Config::ValidationError
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
9
41
|
describe "#remotes" do
|
10
42
|
|
11
43
|
let(:default_options) do
|
@@ -73,8 +105,8 @@ describe Statistrano::Deployment::Strategy::Releases do
|
|
73
105
|
|
74
106
|
config_double = instance_double("Statistrano::Config")
|
75
107
|
expect( Statistrano::Config ).to receive(:new)
|
76
|
-
.with(default_options.merge({remotes: [{hostname: 'web01', remote_dir: 'web01_remote_dir'}]})
|
77
|
-
|
108
|
+
.with( options: default_options.merge({remotes: [{hostname: 'web01', remote_dir: 'web01_remote_dir'}]})
|
109
|
+
.merge(hostname: 'web01', remote_dir: 'web01_remote_dir'))
|
78
110
|
.and_return(config_double)
|
79
111
|
expect( Statistrano::Remote ).to receive(:new)
|
80
112
|
.with(config_double)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: statistrano
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jordan Andree
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-11-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -184,7 +184,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
184
184
|
version: '0'
|
185
185
|
requirements: []
|
186
186
|
rubyforge_project:
|
187
|
-
rubygems_version: 2.4.5
|
187
|
+
rubygems_version: 2.4.5.1
|
188
188
|
signing_key:
|
189
189
|
specification_version: 4
|
190
190
|
summary: deployment tool for static sites
|