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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 57e7b128b972ea3d1ef6479f4eff02bacd010969
4
- data.tar.gz: 433092dffd702bc42225e8b120cebe5eec7f26f0
3
+ metadata.gz: 687953197199b61ff409d6d2ca46af228ddd046b
4
+ data.tar.gz: 8ac0ee0ec0b9637728ca792c6e2b6301bd963acc
5
5
  SHA512:
6
- metadata.gz: e8da835126fc8f176e186dd0e503ce3c39379866257db1a9fffdb965a0232b941ae05e148b37ea60e65691c0375f1bbd9e0e3eccb6c89717661fe483ad833e63
7
- data.tar.gz: 4e29ddaee9c5f1522664e0458472f6c9b511193467520dd774bf8bce4e7f9217836c34024ecf8083984ee118d962a2730c2df3a620774a0ef6eebfd660da1f61
6
+ metadata.gz: ef1939faf699e13754e73d363725f4f8fb9ad448c022e88f4a11c98cb9041f00414f960422426342da392309774385fac921691e19027054e07a38119ae0ba89
7
+ data.tar.gz: bbd659c40f1589b2d7ff3c497f73052c1dc452158309df8b7e73385e8d754a7b0e789ceaa152c1449d8f166ac99e53474b8f6c85e2bdb4728f1a92b9a6a50e45
@@ -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
 
@@ -5,23 +5,42 @@ module Statistrano
5
5
  class Config
6
6
  include RakeTaskWithContextCreation
7
7
 
8
- attr_reader :options
9
- attr_reader :tasks
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=nil, tasks=nil
15
- @options = options.nil? ? {} : options.clone
16
- @tasks = tasks.nil? ? {} : tasks.clone
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
- @options[name] = args[0]
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"
@@ -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 = Shell.run_local "rsync #{rsync_options} " +
67
- "-e ssh #{local_path}/ " +
68
- "#{host_connection}:#{remote_path}/"
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
 
@@ -14,4 +14,4 @@ module Statistrano
14
14
  end
15
15
 
16
16
  end
17
- end
17
+ end
@@ -1,3 +1,3 @@
1
1
  module Statistrano
2
- VERSION = "1.2.3"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -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 & tasks to a blank hash" do
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 & tasks, but clones so the originals don't get modified" do
19
- options = { foo: 'bar' }
20
- tasks = { foo: 'bar' }
21
- subject = described_class.new options, tasks
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.foo = 'baz'
24
- subject.tasks[:foo] = 'baz'
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
- end
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
- .merge(hostname: 'web01', remote_dir: 'web01_remote_dir'))
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.2.3
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-03-17 00:00:00.000000000 Z
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