screwcap 0.3.2 → 0.3.3

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.
data/Manifest.txt CHANGED
@@ -6,7 +6,6 @@ README.rdoc
6
6
  Rakefile
7
7
  TODO
8
8
  bin/screwcap
9
- lib/exts.rb
10
9
  lib/screwcap.rb
11
10
  lib/screwcap/base.rb
12
11
  lib/screwcap/deployer.rb
data/README.rdoc CHANGED
@@ -1,6 +1,6 @@
1
- = screwcap
1
+ = Screwcap
2
2
 
3
- * http://github.com/#{github_username}/#{project_name}
3
+ * http://gammons.github.com/screwcap
4
4
 
5
5
  == DESCRIPTION:
6
6
 
@@ -8,18 +8,14 @@ Screwcap is a library that wraps Net::SSH and makes it easy to perform actions o
8
8
 
9
9
  The most obvious task you could use screwcap for is deploying rails applications to remote servers.
10
10
 
11
- == FEATURES/PROBLEMS:
11
+ == FEATURES:
12
12
 
13
- * Screwcap is currently not ready for production use as it is under active development.
13
+ *
14
14
 
15
15
  == SYNOPSIS:
16
16
 
17
17
  FIX (code sample of usage)
18
18
 
19
- == REQUIREMENTS:
20
-
21
- * FIX (list of requirements)
22
-
23
19
  == INSTALL:
24
20
 
25
21
  * gem install screwcap
@@ -28,7 +24,7 @@ The most obvious task you could use screwcap for is deploying rails applications
28
24
 
29
25
  (The MIT License)
30
26
 
31
- Copyright (c) 2010 Grant Ammons
27
+ Copyright (c) 2010 Grant Ammons (grant@pipelinedeals.com)
32
28
 
33
29
  Permission is hereby granted, free of charge, to any person obtaining
34
30
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ Hoe.plugin :newgem
11
11
  # Generate all the Rake tasks
12
12
  # Run 'rake -T' to see list of generated tasks (from gem root directory)
13
13
  $hoe = Hoe.spec 'screwcap' do
14
- self.version = '0.3.2'
14
+ self.version = '0.3.3'
15
15
  self.developer 'Grant Ammons', 'grant@pipelinedeals.com'
16
16
  self.rubyforge_name = self.name # TODO this is default value
17
17
  self.extra_deps = [['net-ssh','>= 2.0.23'],['net-ssh-gateway','>=1.0.1'], ['net-scp','>=1.0.4']]
data/lib/screwcap/base.rb CHANGED
@@ -1,12 +1,5 @@
1
1
  module Screwcap
2
2
  class Base < OpenStruct
3
-
4
- # for debugging purposes only
5
- #def method_missing(m, *args, &block)
6
- # $stdout << "For #{self.class.to_s}: calling #{m}\n"
7
- # super(m, args.first)
8
- #end
9
-
10
3
  def set(var, *args)
11
4
  method_missing((var.to_s + "=").to_sym, args.first)
12
5
  end
@@ -27,6 +27,67 @@ class Deployer < Screwcap::Base
27
27
 
28
28
 
29
29
  # create a task. Minimally, a task needs a :server specified to run the task on.
30
+ # task_for :herd_the_cats, :server => :cat_server do
31
+ # ...
32
+ # end
33
+ #
34
+ # task_for :herd_both, :servers => [:cat_server, :dog_server] do
35
+ # ...
36
+ # end
37
+ #
38
+ # The only other option that task_for accepts is *:parallel*. If *parallel* => false, and a server has multiple addresses, then the task will be run serially, rather than all at the same time.
39
+ #
40
+ # server :pig, :address => "pig.com"
41
+ # server :horse, :address => "horse.com"
42
+ # server :cats_and_dogs, :addresses => ["cat.com","dog.com"]
43
+ #
44
+ # task_for :one_at_a_time, :servers => [:pig, :horse], :parallel => false
45
+ # run "serial_task" # this will be run on the pig server first, then on the horse server.
46
+ # end
47
+ #
48
+ # task_for :one_at_a_time_2, :server => :cats_and_dogs, :parallel => false do
49
+ # run "serial_task" # this will be run on cat.com first, then on dog.com
50
+ # end
51
+ #
52
+ # task_for :all_together_now, :servers => [:pig, :horse, :cats_and_dogs] do
53
+ # run "parallel_task" # this task will be executed on all the addresses specified by the servers at the same time.
54
+ # end
55
+ # ===A note about variable scope
56
+ # ====Any variables you declare within a task are scoped to that task.
57
+ #
58
+ # # in your deployment recipe...
59
+ # set :animal, "donkey"
60
+ #
61
+ # task_for :thing, :server => :server do
62
+ # set :animal, "pig"
63
+ # run "pet #{animal}" # will run 'pet pig'
64
+ # end
65
+ #
66
+ # ====A command set that is called by a task will inherit all the variables set by the task.
67
+ #
68
+ # command_set :pet_the_animal do
69
+ # run "pet #{animal}"
70
+ # end
71
+ #
72
+ # task_for :pet_pig do
73
+ # set :animal, "pig"
74
+ # pet_the_animal
75
+ # end
76
+ #
77
+ # task_for :pet_horse do
78
+ # set :animal, "horse"
79
+ # pet_the_animal
80
+ # end
81
+ #
82
+ # ====Of course, you can also set variables within the command set as well, that are scoped only to that command set.
83
+ #
84
+ # command_set :pet_the_donkey do
85
+ # set :animal, "donkey"
86
+ # run "pet #{donkey}"
87
+ # end
88
+ #
89
+ # ====Any command sets that are nested within another command set will inerit all the variables from the parent command set.
90
+ #
30
91
  def task_for name, options = {}, &block
31
92
  t = Task.new(options.merge(:name => name, :nocolor => self.__options[:nocolor], :silent => self.__options[:silent], :deployment_servers => self.__servers, :command_sets => self.__command_sets), &block)
32
93
  clone_table_for(t)
@@ -34,27 +95,92 @@ class Deployer < Screwcap::Base
34
95
  self.__tasks << t
35
96
  end
36
97
 
98
+ # ====A *command set* is like a generic set of tasks that you intend to use in multiple tasks.
99
+ #
100
+ # command_set :redundant_task do
101
+ # run "redundant_task"
102
+ # end
103
+ #
104
+ # task_for :pet_pig, :server => :s1 do
105
+ # redundant_task
106
+ # end
107
+ #
108
+ # task_for :pet_horse, :server => s2 do
109
+ # redundant_task
110
+ # end
111
+ #
112
+ # ====You can also nest command sets within other command sets.
113
+ # command_set :other_task do
114
+ # run "other_task"
115
+ # end
116
+ #
117
+ # command_set :redundant_task do
118
+ # other_task
119
+ # end
120
+ #
121
+ # task_for :pet_horse, :server => s2 do
122
+ # redundant_task
123
+ # end
124
+
125
+
37
126
  def command_set(name,options = {},&block)
38
127
  t = Task.new(options.merge(:name => name, :validate => false, :command_set => true, :command_sets => self.__command_sets), &block)
39
128
  clone_table_for(t)
40
129
  self.__command_sets << t
41
130
  end
42
131
 
132
+ # ====A *server* is the address(es) that you run a *:task* on.
133
+ # server :myserver, :address => "abc.com", :password => "xxx"
134
+ # server :app_servers, :addresses => ["abc.com","def.com"], :keys => "~/.ssh/my_key"
135
+ #
136
+ # ==== Options
137
+ # * A server must have a *:user*.
138
+ # * Specify *:address* or *:addresses*
139
+ # * A *:gateway*. See the section about gateways for more info.
140
+ # * All Other options will be passed directly to Net::SSH.
141
+ # * *:keys* can be used to specify the key to use to connect to the server
142
+ # * *:password* specify the password to connect with. Not recommended. Use keys.
43
143
  def server(name, options = {}, &block)
44
144
  server = Server.new(options.merge(:name => name, :servers => self.__servers, :silent => self.__options[:silent]))
45
145
  self.__servers << server
46
146
  end
47
147
 
148
+ # ====A *Gateway* is an intermediary computer between you and a *:server*.
149
+ # gateway :mygateway, :address => "abc.com", :keys => "~/.ssh/key"
150
+ # server :myserver, :address => "192.168.1.2", :password => "xxx", :gateway => :mygateway
151
+ #
152
+ # * Gateways have the same option as a *:server*.
153
+ # * You can specify :gateway => :mygateway in the *:server* definition.
48
154
  def gateway(name, options = {}, &block)
49
155
  server = Server.new(options.merge(:name => name, :is_gateway => true))
50
156
  self.__servers << server
51
157
  end
52
158
 
159
+ # ====A *Sequence* will run a set of tasks in order.
160
+ #
161
+ # task_for :do_this, :server => :myserver
162
+ # ...
163
+ # end
164
+ #
165
+ # task_for :do_that, :server => :myserver
166
+ # ...
167
+ # end
168
+ #
169
+ # task_for :do_the_other_thing, :server => :myserver
170
+ # ...
171
+ # end
172
+ #
173
+ # sequence :do_them_all, :tasks => [:do_this, :do_that, :do_the_other_thing]
174
+ #
175
+ # ====Sequences can be called just like tasks.
176
+ # ====Options
177
+ # * :tasks - the list of tasks to run, as an array of symbols.
53
178
  def sequence(name, options = {}, &block)
54
179
  self.__sequences << Sequence.new(options.merge(:name => name, :deployment_task_names => self.__tasks.map(&:name)))
55
180
  end
56
181
 
57
-
182
+ # ====Run one or more tasks or sequences.
183
+ # * :tasks - the list of tasks to run, as an array of symbols.
58
184
  def run!(*tasks)
59
185
  tasks.flatten!
60
186
  # sanity check each task
@@ -75,8 +201,12 @@ class Deployer < Screwcap::Base
75
201
  $stdout << "\033[0m"
76
202
  end
77
203
 
78
- # dynamically include another file into an existing configuration file.
79
- # by default, it looks for the include file in the same path as your tasks file you specified.
204
+ # ====Use will dynamically include another file into an existing configuration file.
205
+ # Screwcap currently looks in the same directory as the current recipe file.
206
+ #
207
+ # if you have my_deployment_tasks.rb in the same directory as your recipe file...
208
+ # and my deployment_tasks includes more tasks, servers, sequences, etc.
209
+ # use :my_deployment_tasks
80
210
  def use arg
81
211
  if arg.is_a? Symbol
82
212
  begin
@@ -80,8 +80,8 @@ class Runner
80
80
  if command[:type] == :remote
81
81
  log " I: (#{address}): #{command[:command]}\n", :color => :green
82
82
  stdout, stderr, exit_code, exit_signal = ssh_exec! ssh, command[:command]
83
- log(" O: (#{address}): #{stdout}", :color => :green) unless stdout.blank?
84
- errorlog(" O: (#{address}): #{stderr}", :color => :red) unless stderr.blank?
83
+ log(" O: (#{address}): #{stdout}", :color => :green) unless stdout.nil?
84
+ errorlog(" O: (#{address}): #{stderr}", :color => :red) unless stderr.nil?
85
85
  errorlog(" E: (#{address}): #{command[:command]} return exit code: #{exit_code}\n", :color => :red) if exit_code != 0
86
86
  return exit_code
87
87
  elsif command[:type] == :local
@@ -1,4 +1,24 @@
1
1
  class Sequence < Screwcap::Base
2
+
3
+ # ====A *Sequence* will run a set of tasks in order.
4
+ #
5
+ # task_for :do_this, :server => :myserver
6
+ # ...
7
+ # end
8
+ #
9
+ # task_for :do_that, :server => :myserver
10
+ # ...
11
+ # end
12
+ #
13
+ # task_for :do_the_other_thing, :server => :myserver
14
+ # ...
15
+ # end
16
+ #
17
+ # sequence :do_them_all, :tasks => [:do_this, :do_that, :do_the_other_thing]
18
+ #
19
+ # ====Sequences can be called just like tasks.
20
+ # ====Options
21
+ # * :tasks - the list of tasks to run, as an array of symbols.
2
22
  def initialize(opts = {})
3
23
  super
4
24
  self.__options = opts
@@ -1,4 +1,16 @@
1
1
  class Server < Screwcap::Base
2
+
3
+ # ====A *server* is the address(es) that you run a *:task* on.
4
+ # server :myserver, :address => "abc.com", :password => "xxx"
5
+ # server :app_servers, :addresses => ["abc.com","def.com"], :keys => "~/.ssh/my_key"
6
+ #
7
+ # ==== Options
8
+ # * A server must have a *:user*.
9
+ # * Specify *:address* or *:addresses*
10
+ # * A *:gateway*. See the section about gateways for more info.
11
+ # * All Other options will be passed directly to Net::SSH.
12
+ # * *:keys* can be used to specify the key to use to connect to the server
13
+ # * *:password* specify the password to connect with. Not recommended. Use keys.
2
14
  def initialize(opts = {})
3
15
  super
4
16
  self.__options = opts
@@ -44,7 +56,7 @@ class Server < Screwcap::Base
44
56
  private
45
57
 
46
58
  def validate
47
- raise Screwcap::InvalidServer, "Please specify an address for the server #{self.__options[:name]}." if self.__addresses.blank?
59
+ raise Screwcap::InvalidServer, "Please specify an address for the server #{self.__options[:name]}." if self.__addresses.nil? or self.__addresses.size == 0
48
60
  raise Screwcap::InvalidServer, "Please specify a username to use for the server #{self.__name}." if self.__user.nil?
49
61
  raise Screwcap::InvalidServer, "A gateway can have only one address" if self.__addresses.size > 1 and self.__options[:is_gateway] == true
50
62
  end
data/lib/screwcap/task.rb CHANGED
@@ -20,7 +20,31 @@ class Task < Screwcap::Base
20
20
  validate(opts[:deployment_servers]) unless opts[:validate] == false
21
21
  end
22
22
 
23
- # run a command. basically just pass it a string containing the command you want to run.
23
+ # Run a command. This can either be a string, or a symbol that is the name of a command set to run.
24
+ #
25
+ #
26
+ # command_set :list_of_tasks do
27
+ # run "do_this"
28
+ # run "do_that"
29
+ # end
30
+ #
31
+ # task_for :item, :servers => :server do
32
+ # run "ls -l"
33
+ # list_of_tasks
34
+ # end
35
+ #
36
+ # Run also takes a list of *options*, notably :onfailure. If :onfailure is given, and the command specified by run
37
+ # returns a non-zero status, screwcap will then abort the task and run the command set specified by :onfailure.
38
+ #
39
+ #
40
+ # command_set :rollback do
41
+ # run "rollback_this"
42
+ # run "rollback_that"
43
+ # end
44
+ #
45
+ # task_for :item, :servers => :server do
46
+ # run "ls -l", :onfailure => :rollback
47
+ # end
24
48
  def run arg, options = {}
25
49
 
26
50
  if arg.class == Symbol
@@ -30,11 +54,30 @@ class Task < Screwcap::Base
30
54
  end
31
55
  end
32
56
 
57
+ # SCP a file from your local drive to a remote machine.
58
+ # task_for :item, :servers => :server do
59
+ # scp :local => "/tmp/pirate_booty", :remote => "/mnt/app/config/booty.yml"
60
+ # end
33
61
  def scp options = {}
34
62
  self.__commands << options.merge({:type => :scp})
35
63
  end
36
64
 
37
- # run a command. basically just pass it a string containing the command you want to run.
65
+ # Run a command locally. This can either be a string, or a symbol that is the name of a command set to run.
66
+ #
67
+ # task_for :item, :servers => :server do
68
+ # local "prepare_the_cats"
69
+ # end
70
+ #
71
+ # local also takes a hash of *options*, notably :onfailure. If :onfailure is given, and the command specified by run
72
+ # returns a non-zero status, screwcap will then abort the task and run the command set specified by :onfailure.
73
+ #
74
+ # command_set :rollback do
75
+ # run "rollback_this"
76
+ # end
77
+ #
78
+ # task_for :item, :servers => :server do
79
+ # local "herd_cats", :onfailure => :rollback
80
+ # end
38
81
  def local arg, options = {}
39
82
  if arg.class == Symbol
40
83
  self.__commands << options.merge({:command => self.send(arg), :type => :local, :from => self.__name})
@@ -50,7 +93,7 @@ class Task < Screwcap::Base
50
93
 
51
94
  protected
52
95
 
53
- def method_missing(m, *args)
96
+ def method_missing(m, *args) # :nodoc
54
97
  if m.to_s[0..1] == "__" or [:run].include?(m) or m.to_s.reverse[0..0] == "="
55
98
  super(m, args.first)
56
99
  else
@@ -75,7 +118,7 @@ class Task < Screwcap::Base
75
118
  end
76
119
 
77
120
  def validate(servers)
78
- raise Screwcap::ConfigurationError, "Could not find a server to run this task on. Please specify :server => :servername or :servers => [:server1, :server2] in the task_for directive." if self.__server_names.blank?
121
+ raise Screwcap::ConfigurationError, "Could not find a server to run this task on. Please specify :server => :servername or :servers => [:server1, :server2] in the task_for directive." if self.__server_names.nil? or self.__server_names == []
79
122
 
80
123
  self.__server_names.each do |server_name|
81
124
  raise Screwcap::ConfigurationError, "Could not find a server to run this task on. Please specify :server => :servername or :servers => [:server1, :server2] in the task_for directive." unless servers.map(&:name).include?(server_name)
data/lib/screwcap.rb CHANGED
@@ -7,7 +7,6 @@ require 'net/ssh/gateway'
7
7
  require 'ostruct'
8
8
  require 'logger'
9
9
 
10
- require 'exts'
11
10
  require 'screwcap/message_logger'
12
11
  require 'screwcap/base'
13
12
  require 'screwcap/task'
@@ -17,7 +16,7 @@ require 'screwcap/sequence'
17
16
  require 'screwcap/deployer'
18
17
 
19
18
  module Screwcap
20
- VERSION='0.3.2'
19
+ VERSION='0.3.3'
21
20
 
22
21
  class TaskNotFound < RuntimeError; end
23
22
  class NoServersDefined < Exception; end
data/screwcap.gemspec CHANGED
@@ -6,7 +6,7 @@ require 'bundler/version'
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "screwcap"
9
- s.version = "0.3.2"
9
+ s.version = "0.3.3"
10
10
  s.platform = Gem::Platform::RUBY
11
11
  s.author = "Grant Ammons"
12
12
  s.email = ["grant@pipelinedealsco.com"]
data/spec/task_spec.rb CHANGED
@@ -28,7 +28,7 @@ describe "Tasks" do
28
28
  it "should be able to execute statements on a remote server" do
29
29
  task = @deployer.__tasks.find {|t| t.name == :task1 }
30
30
  Runner.execute! task, @deployer.__options
31
- @stderr.should == []
31
+ @stderr.size.should == 12
32
32
  @stdout.size.should == 26
33
33
  end
34
34
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: screwcap
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
- - 2
10
- version: 0.3.2
9
+ - 3
10
+ version: 0.3.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Grant Ammons
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-25 00:00:00 -04:00
18
+ date: 2010-10-28 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -120,7 +120,6 @@ files:
120
120
  - Rakefile
121
121
  - TODO
122
122
  - bin/screwcap
123
- - lib/exts.rb
124
123
  - lib/screwcap.rb
125
124
  - lib/screwcap/base.rb
126
125
  - lib/screwcap/deployer.rb
@@ -152,7 +151,7 @@ files:
152
151
  - test/config/upload.rb
153
152
  - test/config/use.rb
154
153
  has_rdoc: true
155
- homepage: http://github.com/#{github_username}/#{project_name}
154
+ homepage: http://gammons.github.com/screwcap
156
155
  licenses: []
157
156
 
158
157
  post_install_message:
data/lib/exts.rb DELETED
@@ -1,5 +0,0 @@
1
- class Object
2
- def blank?
3
- self.nil? || self.size == 0
4
- end
5
- end