cap-util 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -83,6 +83,89 @@ task :some_rake_task do
83
83
  end
84
84
  ```
85
85
 
86
+ ## GitBranch util
87
+
88
+ This util helps when working with git branches. Right now, it only has a singleton helpers for fetching the current branch the user is on. This can be useful in setting the `:branch` cap var if you always want to deploy from the branch you are currently on.
89
+
90
+ ```ruby
91
+ set (:branch) { CapUtil::GitBranch.current }
92
+ ```
93
+
94
+ ## SharedPath util
95
+
96
+ This utile helps when working with cap's `shared_path`. Right now, it only has a method for removing things from the shared_path
97
+
98
+ ```ruby
99
+ CapUtil::SharedPath.new(self).rm_rf 'cached-copy'
100
+ ```
101
+
102
+ ## Dynamic Server Roles helpers
103
+
104
+ CapUtil has a few utils for fetching and applying cap server roles from dynamic data. It assumes you have your server role data stored in yaml either locally or on some remote location.
105
+
106
+ ### Format
107
+
108
+ The server role yaml needs to be in a format like:
109
+
110
+ ```yaml
111
+ ---
112
+
113
+ app:
114
+ server1: [primary]
115
+ server2: []
116
+
117
+ db:
118
+ server3: [primary, no_release]
119
+ ```
120
+
121
+ ### ServerRolesYaml util
122
+
123
+ This util serves as a base for fetching role yaml data. It provides a framework for subclasses to override and supply the necessary logic to fetch/validate/read yaml data. Use it to pull local or remote config files using the given hooks.
124
+
125
+ ### ServerRoles util
126
+
127
+ This util, given a raw yaml string, will model out the roles and apply them to your cap invocation.
128
+
129
+ ### Example
130
+
131
+ This example shows how a different set of roles could be used for different stages in cap. The yaml config files are located local to the 'my_server_roles_yaml.rb' file.
132
+
133
+ ```ruby
134
+ # in your Capfile...
135
+ require 'my_server_roles_yaml'
136
+ set (:server_roles_yaml) { MyServerRolesYaml.new(self, stage) }
137
+ set (:server_roles) { CapUtil::ServerRoles.new(self, server_roles_yaml.get) }
138
+
139
+ # in my_server_roles_yaml.rb
140
+
141
+ class MyServerRolesYaml < CapUtil::ServerRolesYaml
142
+
143
+ # fetch role data from the local config/#{stage}_server_roles.yaml file
144
+
145
+ def initialize(cap, stage)
146
+ super(cap, :desc => stage)
147
+
148
+ roles_file_name = "#{stage}_server_roles.yaml"
149
+ @roles_file_path = File.expand_path("../../../config/#{roles_file_name}", __FILE__)
150
+ end
151
+
152
+ def validate
153
+ if !valid?
154
+ say_error "`#{@roles_file_path}' does not exist."
155
+ end
156
+ end
157
+
158
+ def valid?
159
+ File.exists?(@roles_file_path)
160
+ end
161
+
162
+ def read
163
+ File.read(@roles_file_path)
164
+ end
165
+
166
+ end
167
+ ```
168
+
86
169
  ## Contributing
87
170
 
88
171
  1. Fork it
@@ -0,0 +1,18 @@
1
+ require 'cap-util'
2
+
3
+ module CapUtil
4
+ class GitBranch
5
+ include CapUtil
6
+
7
+ def self.current(action=:run)
8
+ git_cmd = "git symbolic-ref HEAD"
9
+ if action == :run
10
+ say "Fetching #{color "current git branch", :bold, :cyan} from HEAD"
11
+ (r = run_locally(git_cmd)).success? ? r.stdout.split('/').last.strip : halt
12
+ elsif action == :cmd
13
+ git_cmd
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,85 @@
1
+ require 'yaml'
2
+ require 'cap-util'
3
+
4
+ module CapUtil
5
+
6
+ class ServerRoles
7
+ include CapUtil
8
+ attr_reader :roles
9
+
10
+ def initialize(cap, roles_yaml)
11
+ @cap = cap
12
+ @roles = RoleSet.new(YAML.load(roles_yaml))
13
+ end
14
+
15
+ # Since this is a CapUtil, we can call cap cmds using the `cap` accessor.
16
+ # For each role, call cap's `role` method, passing the relevant values.
17
+
18
+ def apply
19
+ @roles.each do |name, host, opts|
20
+ cap.role name, host, opts
21
+ end
22
+
23
+ end
24
+
25
+ class RoleSet
26
+
27
+ attr_reader :role_defs
28
+
29
+ def initialize(roles_hash)
30
+ @role_defs = roles_hash.map do |(role_name, role_servers_hash)|
31
+ RoleDef.new(role_name, role_servers_hash)
32
+ end
33
+ end
34
+
35
+ def each(&block)
36
+ @role_defs.each {|role_def| role_def.apply(&block)}
37
+ end
38
+
39
+ end
40
+
41
+ class RoleDef
42
+
43
+ attr_reader :name, :servers
44
+
45
+ def initialize(name, servers_hash)
46
+ @name = name
47
+ @servers = servers_hash.map do |(server_name, server_options_list)|
48
+ ServerDef.new(server_name, server_options_list)
49
+ end
50
+ end
51
+
52
+ def apply(&block)
53
+ @servers.each do |server|
54
+ block.call @name, server.hostname, server.options
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ class ServerDef
61
+
62
+ attr_reader :hostname, :options
63
+
64
+ def initialize(name, options_list=[])
65
+ @hostname = "#{name}.reelfx.com"
66
+ @options = {}
67
+
68
+ # so, weird cap bug. options have to match type when using them in
69
+ # a task's definition. so if you have (string) 'primary' option, you
70
+ # have to use a string in your task defs.
71
+ # this is not the case for the role names (string or symbol works).
72
+ # so, I'm just defining each option, both in string (how it comes from
73
+ # the configs) and symbol form.
74
+
75
+ options_list.each do |option|
76
+ @options[option.to_s] = true
77
+ @options[option.to_sym] = true
78
+ end
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+
85
+ end
@@ -0,0 +1,32 @@
1
+ require 'cap-util'
2
+
3
+ module CapUtil
4
+
5
+ # the class should be use as a superclass for fetching server roles yaml.
6
+
7
+ class ServerRolesYaml
8
+ include CapUtil
9
+ attr_reader :desc, :source
10
+
11
+ def initialize(cap, opts=nil)
12
+ opts ||= {}
13
+
14
+ @cap = cap
15
+ @desc = opts[:desc] ? "#{opts[:desc]} server roles" : "server roles"
16
+ @source = opts[:source] ? " from #{opts[:source]}" : ""
17
+ end
18
+
19
+ def get
20
+ say "Applying #{color @desc, :bold, :cyan}#{@source}."
21
+
22
+ validate
23
+ valid? ? read : halt
24
+ end
25
+
26
+ def validate; raise NotImplementedError; end
27
+ def valid?; raise NotImplementedError; end
28
+ def read; raise NotImplementedError; end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,22 @@
1
+ require 'cap-util'
2
+
3
+ module CapUtil
4
+
5
+ # SharedPath models the path to the set of shared deployed code. named
6
+ # after cap's `shared_path` method b/c they point at similar locations.
7
+
8
+ class SharedPath
9
+ include CapUtil
10
+
11
+ def initialize(cap, path=nil)
12
+ @cap = cap
13
+ @shared_path = File.expand_path(path || cap.shared_path)
14
+ end
15
+
16
+ def rm_rf(rel_path)
17
+ run "rm -rf #{@shared_path}/#{rel_path}"
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -1,3 +1,3 @@
1
1
  module CapUtil
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -0,0 +1,19 @@
1
+ require 'assert'
2
+
3
+ require 'cap-util/git_branch'
4
+
5
+ module CapUtil
6
+
7
+ class GitBranchTests < Assert::Context
8
+ desc "the GitBranch util"
9
+ subject { GitBranch }
10
+
11
+ should have_imeth :current
12
+
13
+ should "use the symbolic ref the HEAD is at for the current branch" do
14
+ assert_equal "git symbolic-ref HEAD", subject.current(:cmd)
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,120 @@
1
+ require 'assert'
2
+
3
+ require 'cap-util/server_roles'
4
+
5
+ module CapUtil
6
+
7
+ class ServerRolesTests < Assert::Context
8
+ desc "the ServerRoles handler"
9
+ setup do
10
+ roles_yaml = <<YAML
11
+ ---
12
+ hosts:
13
+ host1: [primary]
14
+ host2: []
15
+ YAML
16
+ @fake_cap = FakeCap.new
17
+ @server_roles = ServerRoles.new(@fake_cap, roles_yaml)
18
+ end
19
+ subject { @server_roles }
20
+
21
+ should have_reader :roles
22
+ should have_imeth :apply
23
+
24
+ should "build cap roles from yaml using the apply meth" do
25
+ subject.apply
26
+ roles = @fake_cap.roles.sort{|a,b| a[1] <=> b[1]}
27
+
28
+ assert_equal 2, @fake_cap.roles.size
29
+ assert_equal ['hosts', 'host2.reelfx.com', {}], roles.last
30
+ end
31
+
32
+ end
33
+
34
+ class ServerRoles
35
+
36
+ class RoleSetTests < Assert::Context
37
+ desc "the server roles RoleSet"
38
+ setup do
39
+ @roles_hash = {
40
+ 'hosts' => {
41
+ 'host1' => ['primary'],
42
+ 'host2' => []
43
+ }
44
+ }
45
+ @role_set = RoleSet.new @roles_hash
46
+ end
47
+ subject { @role_set }
48
+
49
+ should have_reader :role_defs
50
+ should have_imeth :each
51
+
52
+ should "yield the name, hostname, and opts for each host/server when iterating the roles" do
53
+ exp = [
54
+ ['hosts', 'host1.reelfx.com', {:primary => true, 'primary' => true}],
55
+ ['hosts', 'host2.reelfx.com', {}]
56
+ ]
57
+ actual = []
58
+ subject.each do |name, host, opts|
59
+ actual << [name, host, opts]
60
+ end
61
+
62
+ assert_equal exp, actual.sort{|a, b| a[1] <=> b[1]}
63
+ end
64
+ end
65
+
66
+ class RoleDefTests < RoleSetTests
67
+ desc "the server roles RoleDef"
68
+ setup do
69
+ @role_servers_hash = {
70
+ 'host1' => ['primary'],
71
+ 'host2' => []
72
+ }
73
+ @role = RoleDef.new('hosts', @role_servers_hash)
74
+ end
75
+ subject { @role }
76
+
77
+ should have_reader :name, :servers
78
+ should have_imeth :apply
79
+
80
+ should "build a set of servers from a definition hash" do
81
+ servs = subject.servers.sort{|a, b| a.hostname <=> b.hostname }
82
+
83
+ assert_kind_of ::Array, servs
84
+ assert_equal 2, servs.size
85
+ assert_kind_of ServerDef, servs.first
86
+ assert_equal 'host2.reelfx.com', servs.last.hostname
87
+ assert_equal true, servs.first.options['primary']
88
+ end
89
+
90
+ end
91
+
92
+ class ServerDefTests < Assert::Context
93
+ desc "the server roles ServerDef"
94
+ setup do
95
+ @server = ServerDef.new('host1')
96
+ end
97
+ subject { @server }
98
+
99
+ should have_readers :hostname, :options
100
+
101
+ should "build its hostname from the server name" do
102
+ assert_equal "host1.reelfx.com", subject.hostname
103
+ end
104
+
105
+ should "have no options by defoult" do
106
+ assert_empty subject.options
107
+ end
108
+
109
+ should "build its options with both string and symbol keys" do
110
+ server = ServerDef.new('opts1', 'primary')
111
+
112
+ assert_equal true, server.options[:primary]
113
+ assert_equal true, server.options['primary']
114
+ end
115
+
116
+ end
117
+
118
+ end
119
+
120
+ end
@@ -0,0 +1,39 @@
1
+ require 'assert'
2
+
3
+ require 'cap-util/server_roles_yaml'
4
+
5
+ module CapUtil
6
+
7
+ class ServerRolesYamlTests < Assert::Context
8
+ desc "the ServerRolesYaml util"
9
+ setup do
10
+ @roles_yaml = ServerRolesYaml.new(FakeCap.new )
11
+ end
12
+ subject { @roles_yaml }
13
+
14
+ should have_imeths :get, :validate, :valid?, :read
15
+ should have_reader :desc, :source
16
+
17
+ should "default the yaml's desc and source" do
18
+ assert_equal "server roles", subject.desc
19
+ assert_equal "", subject.source
20
+ end
21
+
22
+ should "use a custom desc and source if given" do
23
+ yml = ServerRolesYaml.new(FakeCap.new, {
24
+ :desc => 'staging',
25
+ :source => 'the place'
26
+ })
27
+ assert_equal "staging server roles", yml.desc
28
+ assert_equal " from the place", yml.source
29
+ end
30
+
31
+ should "raise appropriate NotImplementedErrors" do
32
+ assert_raises(NotImplementedError) { subject.validate }
33
+ assert_raises(NotImplementedError) { subject.valid? }
34
+ assert_raises(NotImplementedError) { subject.read }
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,30 @@
1
+ require 'assert'
2
+ require 'fileutils'
3
+ require 'pathname'
4
+
5
+ require 'cap-util/shared_path'
6
+
7
+ module CapUtil
8
+
9
+ class SharedPathTests < Assert::Context
10
+ desc "the SharedPath util"
11
+ setup do
12
+ @fake_cap = FakeCap.new
13
+ @fake_cap.shared_path = Pathname.new File.expand_path("tmp/shared")
14
+
15
+ @shared_path = SharedPath.new(@fake_cap)
16
+ end
17
+ subject { @shared_path }
18
+
19
+ should have_imeths :rm_rf
20
+
21
+ should "remove a given relative path with the `rm_rf` method" do
22
+ subject.rm_rf 'cached-copy'
23
+ exp_cmd = "rm -rf #{File.expand_path("tmp/shared/cached-copy")}"
24
+
25
+ assert_equal [exp_cmd], @fake_cap.cmds_run.last
26
+ end
27
+
28
+ end
29
+
30
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cap-util
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 15
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 3
8
+ - 4
9
9
  - 0
10
- version: 0.3.0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Kelly Redding
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-10-16 00:00:00 Z
18
+ date: 2012-10-18 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: assert
@@ -78,19 +78,27 @@ files:
78
78
  - cap-util.gemspec
79
79
  - lib/cap-util.rb
80
80
  - lib/cap-util/fake_cap.rb
81
+ - lib/cap-util/git_branch.rb
81
82
  - lib/cap-util/halt.rb
82
83
  - lib/cap-util/rake_task.rb
83
84
  - lib/cap-util/run.rb
84
85
  - lib/cap-util/say.rb
86
+ - lib/cap-util/server_roles.rb
87
+ - lib/cap-util/server_roles_yaml.rb
88
+ - lib/cap-util/shared_path.rb
85
89
  - lib/cap-util/time.rb
86
90
  - lib/cap-util/timer.rb
87
91
  - lib/cap-util/unset_var.rb
88
92
  - lib/cap-util/version.rb
89
93
  - test/cap_util_tests.rb
90
94
  - test/fake_cap_tests.rb
95
+ - test/git_branch_tests.rb
91
96
  - test/helper.rb
92
97
  - test/irb.rb
93
98
  - test/rake_task_tests.rb
99
+ - test/server_roles_tests.rb
100
+ - test/server_roles_yaml_tests.rb
101
+ - test/shared_path_tests.rb
94
102
  - test/timer_tests.rb
95
103
  - test/unset_var_tests.rb
96
104
  homepage: http://github.com/redding/cap-util
@@ -129,8 +137,12 @@ summary: A set of utilities for writing cap tasks.
129
137
  test_files:
130
138
  - test/cap_util_tests.rb
131
139
  - test/fake_cap_tests.rb
140
+ - test/git_branch_tests.rb
132
141
  - test/helper.rb
133
142
  - test/irb.rb
134
143
  - test/rake_task_tests.rb
144
+ - test/server_roles_tests.rb
145
+ - test/server_roles_yaml_tests.rb
146
+ - test/shared_path_tests.rb
135
147
  - test/timer_tests.rb
136
148
  - test/unset_var_tests.rb