necktie 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/necktie +1 -1
- data/lib/necktie/rush.rb +11 -0
- data/lib/necktie/services.rb +31 -11
- data/lib/necktie.rb +10 -6
- data/necktie.gemspec +2 -2
- data/rush/README.rdoc +87 -0
- data/rush/Rakefile +60 -0
- data/rush/VERSION +1 -0
- data/rush/bin/rush +13 -0
- data/rush/bin/rushd +7 -0
- data/rush/lib/rush/access.rb +130 -0
- data/rush/lib/rush/array_ext.rb +19 -0
- data/rush/lib/rush/box.rb +112 -0
- data/rush/lib/rush/commands.rb +55 -0
- data/rush/lib/rush/config.rb +154 -0
- data/rush/lib/rush/dir.rb +160 -0
- data/rush/lib/rush/embeddable_shell.rb +26 -0
- data/rush/lib/rush/entry.rb +185 -0
- data/rush/lib/rush/exceptions.rb +31 -0
- data/rush/lib/rush/file.rb +85 -0
- data/rush/lib/rush/find_by.rb +39 -0
- data/rush/lib/rush/fixnum_ext.rb +18 -0
- data/rush/lib/rush/head_tail.rb +11 -0
- data/rush/lib/rush/local.rb +402 -0
- data/rush/lib/rush/process.rb +59 -0
- data/rush/lib/rush/process_set.rb +62 -0
- data/rush/lib/rush/remote.rb +156 -0
- data/rush/lib/rush/search_results.rb +58 -0
- data/rush/lib/rush/server.rb +117 -0
- data/rush/lib/rush/shell.rb +187 -0
- data/rush/lib/rush/ssh_tunnel.rb +122 -0
- data/rush/lib/rush/string_ext.rb +3 -0
- data/rush/lib/rush.rb +87 -0
- data/rush/rush.gemspec +113 -0
- data/rush/spec/access_spec.rb +134 -0
- data/rush/spec/array_ext_spec.rb +15 -0
- data/rush/spec/base.rb +24 -0
- data/rush/spec/box_spec.rb +64 -0
- data/rush/spec/commands_spec.rb +47 -0
- data/rush/spec/config_spec.rb +108 -0
- data/rush/spec/dir_spec.rb +164 -0
- data/rush/spec/embeddable_shell_spec.rb +17 -0
- data/rush/spec/entry_spec.rb +133 -0
- data/rush/spec/file_spec.rb +83 -0
- data/rush/spec/find_by_spec.rb +58 -0
- data/rush/spec/fixnum_ext_spec.rb +19 -0
- data/rush/spec/local_spec.rb +352 -0
- data/rush/spec/process_set_spec.rb +50 -0
- data/rush/spec/process_spec.rb +73 -0
- data/rush/spec/remote_spec.rb +140 -0
- data/rush/spec/rush_spec.rb +28 -0
- data/rush/spec/search_results_spec.rb +44 -0
- data/rush/spec/shell_spec.rb +23 -0
- data/rush/spec/ssh_tunnel_spec.rb +122 -0
- data/rush/spec/string_ext_spec.rb +23 -0
- metadata +53 -3
- data/lib/necktie/files.rb +0 -14
data/bin/necktie
CHANGED
data/lib/necktie/rush.rb
ADDED
data/lib/necktie/services.rb
CHANGED
@@ -1,13 +1,33 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
system "
|
5
|
-
|
6
|
-
|
1
|
+
module Services
|
2
|
+
def self.start(name)
|
3
|
+
puts " ** Starting service #{name}"
|
4
|
+
system "update-rc.d #{name} defaults" and
|
5
|
+
system "service #{name} start" or
|
6
|
+
fail "failed to start #{name}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.enable(name)
|
10
|
+
system "update-rc.d #{name} defaults" or "cannot enable #{name}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.stop(name)
|
14
|
+
puts " ** Stopping service #{name}"
|
15
|
+
system "service #{name} stop" and
|
16
|
+
system "update-rc.d -f #{name} remove" or
|
17
|
+
fail "failed to stop #{name}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.disable(name)
|
21
|
+
system "update-rc.d -f #{name} remove" or fail "cannot disable #{name}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.restart(name)
|
25
|
+
puts " ** Stopping service #{name}"
|
26
|
+
system "service #{name} restart" or fail "failed to restart #{name}"
|
27
|
+
end
|
7
28
|
|
8
|
-
def
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
fail "failed to shutdown #{name}"
|
29
|
+
def self.status(name)
|
30
|
+
status = File.read("|sudo service --status-all 2>&1")[/^ \[ (.) \] #{Regexp.escape name}$/,1]
|
31
|
+
stauts == "+" ? true : status == "-" ? false : nil
|
32
|
+
end
|
13
33
|
end
|
data/lib/necktie.rb
CHANGED
@@ -2,7 +2,10 @@ require "fileutils"
|
|
2
2
|
extend FileUtils
|
3
3
|
require "necktie/gems"
|
4
4
|
require "necktie/services"
|
5
|
-
require "necktie/
|
5
|
+
require "necktie/rush"
|
6
|
+
|
7
|
+
puts launch_dir
|
8
|
+
exit!
|
6
9
|
|
7
10
|
def necktie(args)
|
8
11
|
etc = "/etc/necktie"
|
@@ -35,8 +38,9 @@ def necktie(args)
|
|
35
38
|
Dir.chdir repo do
|
36
39
|
executed = File.exist?(etc) ? File.read(etc).split("\n") : []
|
37
40
|
roles.each do |role|
|
38
|
-
|
39
|
-
|
41
|
+
skip = executed.map { |t| t[/^#{Regexp.escape role}\/(.*)$/, 1] }.reject(&:nil?)
|
42
|
+
tasks = Dir["tasks/#{role}/*.rb"].sort.map { |name| File.basename(name).gsub(/\.rb$/, "") }
|
43
|
+
todo = tasks - skip
|
40
44
|
if tasks.empty?
|
41
45
|
puts " * No tasks for role #{role}"
|
42
46
|
elsif todo.empty?
|
@@ -45,9 +49,9 @@ def necktie(args)
|
|
45
49
|
puts " * Now in role: #{role}"
|
46
50
|
File.open etc, "a" do |f|
|
47
51
|
todo.each do |task|
|
48
|
-
puts " ** Executing #{task
|
49
|
-
load "tasks/#{task}.rb"
|
50
|
-
f.puts task
|
52
|
+
puts " ** Executing #{task}"
|
53
|
+
load "tasks/#{role}/#{task}.rb"
|
54
|
+
f.puts task if task[/^\d+_/]
|
51
55
|
end
|
52
56
|
end
|
53
57
|
end
|
data/necktie.gemspec
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = "necktie"
|
3
|
-
spec.version = "0.
|
3
|
+
spec.version = "0.2.0"
|
4
4
|
spec.author = "Assaf Arkin"
|
5
5
|
spec.email = "assaf@labnotes.org"
|
6
6
|
spec.homepage = ""
|
7
7
|
spec.summary = "Dress to impress"
|
8
8
|
spec.description = ""
|
9
9
|
|
10
|
-
spec.files = Dir["{bin,lib}/**/*", "*.{gemspec,rdoc}"]
|
10
|
+
spec.files = Dir["{bin,lib,rush}/**/*", "*.{gemspec,rdoc}"]
|
11
11
|
spec.executable = "necktie"
|
12
12
|
end
|
data/rush/README.rdoc
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
= rush -- manage your unix systems with pure Ruby
|
2
|
+
|
3
|
+
rush is a unix integration library and an interactive shell which uses pure Ruby syntax. Walk directory trees; create, copy, search, and destroy files; find and kill processes - everything you'd normally do with shell commands, now in the strict and elegant world of Ruby.
|
4
|
+
|
5
|
+
== Usage
|
6
|
+
|
7
|
+
Count the number of classes in your project using bash:
|
8
|
+
|
9
|
+
find myproj -name \*.rb | xargs grep '^\s*class' | wc -l
|
10
|
+
|
11
|
+
In rush, this is:
|
12
|
+
|
13
|
+
myproj['**/*.rb'].search(/^\s*class/).lines.size
|
14
|
+
|
15
|
+
Pesky stray mongrels? In bash:
|
16
|
+
|
17
|
+
kill `ps aux | grep mongrel_rails | grep -v grep | cut -c 10-20`
|
18
|
+
|
19
|
+
In rush:
|
20
|
+
|
21
|
+
processes.filter(:cmdline => /mongrel_rails/).kill
|
22
|
+
|
23
|
+
== As a library
|
24
|
+
|
25
|
+
require 'rubygems'
|
26
|
+
require 'rush'
|
27
|
+
|
28
|
+
file = Rush['/tmp/myfile']
|
29
|
+
file.write "hello"
|
30
|
+
puts file.contents
|
31
|
+
file.destroy
|
32
|
+
|
33
|
+
puts Rush.my_process.pid
|
34
|
+
puts Rush.processes.size
|
35
|
+
puts Rush.bash("echo SHELL COMMAND | tr A-Z a-z")
|
36
|
+
puts Rush.launch_dir['*.rb'].search(/Rush/).entries.inspect
|
37
|
+
|
38
|
+
== Invoking the shell
|
39
|
+
|
40
|
+
Run the "rush" binary to enter the interactive shell.
|
41
|
+
|
42
|
+
== Remote access and clustering
|
43
|
+
|
44
|
+
rush can control any number of remote machines from a single location. Copy files or directories between servers as seamlessly as if it was all local.
|
45
|
+
|
46
|
+
Example of remote access:
|
47
|
+
|
48
|
+
local = Rush::Box.new('localhost')
|
49
|
+
remote = Rush::Box.new('my.remote.server.com')
|
50
|
+
local_dir = local['/Users/adam/myproj/']
|
51
|
+
remote_dir = remote['/home/myproj/app/']
|
52
|
+
|
53
|
+
local_dir.copy_to remote_dir
|
54
|
+
remote_dir['**/.svn/'].each { |d| d.destroy }
|
55
|
+
|
56
|
+
Clustering:
|
57
|
+
|
58
|
+
local_dir = Rush::Box.new('localhost')['/Users/adam/server_logs/'].create
|
59
|
+
servers = %w(www1 www2 www3).map { |n| Rush::Box.new(n) }
|
60
|
+
servers.each { |s| s['/var/log/nginx/access.log'].copy_to local_dir["#{s.host}_access.log"] }
|
61
|
+
|
62
|
+
== Reference
|
63
|
+
|
64
|
+
For more details on syntax and commands, see:
|
65
|
+
|
66
|
+
* Rush
|
67
|
+
* Rush::Entry
|
68
|
+
* Rush::File
|
69
|
+
* Rush::Dir
|
70
|
+
* Rush::Commands
|
71
|
+
* Rush::Box
|
72
|
+
* Rush::Process
|
73
|
+
|
74
|
+
== Meta
|
75
|
+
|
76
|
+
Created by Adam Wiggins
|
77
|
+
|
78
|
+
Patches contributed by Chihiro Ito, Gabriel Ware, Michael Schutte, Ricardo Chimal Jr., and Nicholas Schlueter, Pedro Belo, and Martin Kuehl
|
79
|
+
|
80
|
+
Logo by James Lindenbaum
|
81
|
+
|
82
|
+
Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
|
83
|
+
|
84
|
+
http://rush.heroku.com
|
85
|
+
|
86
|
+
http://groups.google.com/group/ruby-shell
|
87
|
+
|
data/rush/Rakefile
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
require 'jeweler'
|
4
|
+
|
5
|
+
Jeweler::Tasks.new do |s|
|
6
|
+
s.name = "rush"
|
7
|
+
s.summary = "A Ruby replacement for bash+ssh."
|
8
|
+
s.description = "A Ruby replacement for bash+ssh, providing both an interactive shell and a library. Manage both local and remote unix systems from a single client."
|
9
|
+
s.author = "Adam Wiggins"
|
10
|
+
s.email = "adam@heroku.com"
|
11
|
+
s.homepage = "http://rush.heroku.com/"
|
12
|
+
s.executables = [ "rush", "rushd" ]
|
13
|
+
s.rubyforge_project = "ruby-shell"
|
14
|
+
s.has_rdoc = true
|
15
|
+
|
16
|
+
s.add_dependency 'session'
|
17
|
+
|
18
|
+
s.files = FileList["[A-Z]*", "{bin,lib,spec}/**/*"]
|
19
|
+
end
|
20
|
+
|
21
|
+
Jeweler::RubyforgeTasks.new
|
22
|
+
|
23
|
+
######################################################
|
24
|
+
|
25
|
+
require 'spec/rake/spectask'
|
26
|
+
|
27
|
+
desc "Run all specs"
|
28
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
29
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "Print specdocs"
|
33
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
34
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
35
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "Run all examples with RCov"
|
39
|
+
Spec::Rake::SpecTask.new('rcov') do |t|
|
40
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
41
|
+
t.rcov = true
|
42
|
+
t.rcov_opts = ['--exclude', 'examples']
|
43
|
+
end
|
44
|
+
|
45
|
+
task :default => :spec
|
46
|
+
|
47
|
+
######################################################
|
48
|
+
|
49
|
+
require 'rake/rdoctask'
|
50
|
+
|
51
|
+
Rake::RDocTask.new do |t|
|
52
|
+
t.rdoc_dir = 'rdoc'
|
53
|
+
t.title = "rush, a Ruby replacement for bash+ssh"
|
54
|
+
t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
55
|
+
t.options << '--charset' << 'utf-8'
|
56
|
+
t.rdoc_files.include('README.rdoc')
|
57
|
+
t.rdoc_files.include('lib/rush.rb')
|
58
|
+
t.rdoc_files.include('lib/rush/*.rb')
|
59
|
+
end
|
60
|
+
|
data/rush/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.6.2
|
data/rush/bin/rush
ADDED
data/rush/bin/rushd
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
# A class to hold permissions (read, write, execute) for files and dirs.
|
2
|
+
# See Rush::Entry#access= for information on the public-facing interface.
|
3
|
+
class Rush::Access
|
4
|
+
attr_accessor :user_can_read, :user_can_write, :user_can_execute
|
5
|
+
attr_accessor :group_can_read, :group_can_write, :group_can_execute
|
6
|
+
attr_accessor :other_can_read, :other_can_write, :other_can_execute
|
7
|
+
|
8
|
+
def self.roles
|
9
|
+
%w(user group other)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.permissions
|
13
|
+
%w(read write execute)
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse(options)
|
17
|
+
options.each do |key, value|
|
18
|
+
next unless m = key.to_s.match(/(.*)_can$/)
|
19
|
+
key = m[1].to_sym
|
20
|
+
roles = extract_list('role', key, self.class.roles)
|
21
|
+
perms = extract_list('permission', value, self.class.permissions)
|
22
|
+
set_matrix(perms, roles)
|
23
|
+
end
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.parse(options)
|
28
|
+
new.parse(options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def apply(full_path)
|
32
|
+
FileUtils.chmod(octal_permissions, full_path)
|
33
|
+
rescue Errno::ENOENT
|
34
|
+
raise Rush::DoesNotExist, full_path
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_hash
|
38
|
+
hash = {}
|
39
|
+
self.class.roles.each do |role|
|
40
|
+
self.class.permissions.each do |perm|
|
41
|
+
key = "#{role}_can_#{perm}".to_sym
|
42
|
+
hash[key] = send(key) ? 1 : 0
|
43
|
+
end
|
44
|
+
end
|
45
|
+
hash
|
46
|
+
end
|
47
|
+
|
48
|
+
def display_hash
|
49
|
+
hash = {}
|
50
|
+
to_hash.each do |key, value|
|
51
|
+
hash[key] = true if value == 1
|
52
|
+
end
|
53
|
+
hash
|
54
|
+
end
|
55
|
+
|
56
|
+
def from_hash(hash)
|
57
|
+
self.class.roles.each do |role|
|
58
|
+
self.class.permissions.each do |perm|
|
59
|
+
key = "#{role}_can_#{perm}"
|
60
|
+
send("#{key}=".to_sym, hash[key.to_sym].to_i == 1 ? true : false)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.from_hash(hash)
|
67
|
+
new.from_hash(hash)
|
68
|
+
end
|
69
|
+
|
70
|
+
def octal_permissions
|
71
|
+
perms = [ 0, 0, 0 ]
|
72
|
+
perms[0] += 4 if user_can_read
|
73
|
+
perms[0] += 2 if user_can_write
|
74
|
+
perms[0] += 1 if user_can_execute
|
75
|
+
|
76
|
+
perms[1] += 4 if group_can_read
|
77
|
+
perms[1] += 2 if group_can_write
|
78
|
+
perms[1] += 1 if group_can_execute
|
79
|
+
|
80
|
+
perms[2] += 4 if other_can_read
|
81
|
+
perms[2] += 2 if other_can_write
|
82
|
+
perms[2] += 1 if other_can_execute
|
83
|
+
|
84
|
+
eval("0" + perms.join)
|
85
|
+
end
|
86
|
+
|
87
|
+
def from_octal(mode)
|
88
|
+
perms = octal_integer_array(mode)
|
89
|
+
|
90
|
+
self.user_can_read = (perms[0] & 4) > 0 ? true : false
|
91
|
+
self.user_can_write = (perms[0] & 2) > 0 ? true : false
|
92
|
+
self.user_can_execute = (perms[0] & 1) > 0 ? true : false
|
93
|
+
|
94
|
+
self.group_can_read = (perms[1] & 4) > 0 ? true : false
|
95
|
+
self.group_can_write = (perms[1] & 2) > 0 ? true : false
|
96
|
+
self.group_can_execute = (perms[1] & 1) > 0 ? true : false
|
97
|
+
|
98
|
+
self.other_can_read = (perms[2] & 4) > 0 ? true : false
|
99
|
+
self.other_can_write = (perms[2] & 2) > 0 ? true : false
|
100
|
+
self.other_can_execute = (perms[2] & 1) > 0 ? true : false
|
101
|
+
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
def octal_integer_array(mode)
|
106
|
+
mode %= 01000 # filter out everything but the bottom three digits
|
107
|
+
mode = sprintf("%o", mode) # convert to string
|
108
|
+
mode.split("").map { |p| p.to_i } # and finally, array of integers
|
109
|
+
end
|
110
|
+
|
111
|
+
def set_matrix(perms, roles)
|
112
|
+
perms.each do |perm|
|
113
|
+
roles.each do |role|
|
114
|
+
meth = "#{role}_can_#{perm}=".to_sym
|
115
|
+
send(meth, true)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def extract_list(type, value, choices)
|
121
|
+
list = parts_from(value)
|
122
|
+
list.each do |value|
|
123
|
+
raise(Rush::BadAccessSpecifier, "Unrecognized #{type}: #{value}") unless choices.include? value
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def parts_from(value)
|
128
|
+
value.to_s.split('_').reject { |r| r == 'and' }
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Rush mixes in Rush::Commands in order to allow operations on groups of
|
2
|
+
# Rush::Entry items. For example, dir['**/*.rb'] returns an array of files, so
|
3
|
+
# dir['**/*.rb'].destroy would destroy all the files specified.
|
4
|
+
#
|
5
|
+
# One cool tidbit: the array can contain entries anywhere, so you can create
|
6
|
+
# collections of entries from different servers and then operate across them:
|
7
|
+
#
|
8
|
+
# [ box1['/var/log/access.log'] + box2['/var/log/access.log'] ].search /#{url}/
|
9
|
+
class Array
|
10
|
+
include Rush::Commands
|
11
|
+
|
12
|
+
def entries
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
include Rush::FindBy
|
17
|
+
|
18
|
+
include Rush::HeadTail
|
19
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# A rush box is a single unix machine - a server, workstation, or VPS instance.
|
2
|
+
#
|
3
|
+
# Specify a box by hostname (default = 'localhost'). If the box is remote, the
|
4
|
+
# first action performed will attempt to open an ssh tunnel. Use square
|
5
|
+
# brackets to access the filesystem, or processes to access the process list.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
# local = Rush::Box.new
|
10
|
+
# local['/etc/hosts'].contents
|
11
|
+
# local.processes
|
12
|
+
#
|
13
|
+
class Rush::Box
|
14
|
+
attr_reader :host
|
15
|
+
|
16
|
+
# Instantiate a box. No action is taken to make a connection until you try
|
17
|
+
# to perform an action. If the box is remote, an ssh tunnel will be opened.
|
18
|
+
# Specify a username with the host if the remote ssh user is different from
|
19
|
+
# the local one (e.g. Rush::Box.new('user@host')).
|
20
|
+
def initialize(host='localhost')
|
21
|
+
@host = host
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s # :nodoc:
|
25
|
+
host
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspect # :nodoc:
|
29
|
+
host
|
30
|
+
end
|
31
|
+
|
32
|
+
# Access / on the box.
|
33
|
+
def filesystem
|
34
|
+
Rush::Entry.factory('/', self)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Look up an entry on the filesystem, e.g. box['/path/to/some/file'].
|
38
|
+
# Returns a subclass of Rush::Entry - either Rush::Dir if you specifiy
|
39
|
+
# trailing slash, or Rush::File otherwise.
|
40
|
+
def [](key)
|
41
|
+
filesystem[key]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Get the list of processes running on the box, not unlike "ps aux" in bash.
|
45
|
+
# Returns a Rush::ProcessSet.
|
46
|
+
def processes
|
47
|
+
Rush::ProcessSet.new(
|
48
|
+
connection.processes.map do |ps|
|
49
|
+
Rush::Process.new(ps, self)
|
50
|
+
end
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Execute a command in the standard unix shell. Returns the contents of
|
55
|
+
# stdout if successful, or raises Rush::BashFailed with the output of stderr
|
56
|
+
# if the shell returned a non-zero value. Options:
|
57
|
+
#
|
58
|
+
# :user => unix username to become via sudo
|
59
|
+
# :env => hash of environment variables
|
60
|
+
# :background => run in the background (returns Rush::Process instead of stdout)
|
61
|
+
#
|
62
|
+
# Examples:
|
63
|
+
#
|
64
|
+
# box.bash '/etc/init.d/mysql restart', :user => 'root'
|
65
|
+
# box.bash 'rake db:migrate', :user => 'www', :env => { :RAILS_ENV => 'production' }
|
66
|
+
# box.bash 'mongrel_rails start', :background => true
|
67
|
+
#
|
68
|
+
def bash(command, options={})
|
69
|
+
cmd_with_env = command_with_environment(command, options[:env])
|
70
|
+
|
71
|
+
if options[:background]
|
72
|
+
pid = connection.bash(cmd_with_env, options[:user], true)
|
73
|
+
processes.find_by_pid(pid)
|
74
|
+
else
|
75
|
+
connection.bash(cmd_with_env, options[:user], false)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def command_with_environment(command, env) # :nodoc:
|
80
|
+
return command unless env
|
81
|
+
|
82
|
+
vars = env.map do |key, value|
|
83
|
+
"export #{key}='#{value}'"
|
84
|
+
end
|
85
|
+
vars.push(command).join("\n")
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns true if the box is responding to commands.
|
89
|
+
def alive?
|
90
|
+
connection.alive?
|
91
|
+
end
|
92
|
+
|
93
|
+
# This is called automatically the first time an action is invoked, but you
|
94
|
+
# may wish to call it manually ahead of time in order to have the tunnel
|
95
|
+
# already set up and running. You can also use this to pass a timeout option,
|
96
|
+
# either :timeout => (seconds) or :timeout => :infinite.
|
97
|
+
def establish_connection(options={})
|
98
|
+
connection.ensure_tunnel(options)
|
99
|
+
end
|
100
|
+
|
101
|
+
def connection # :nodoc:
|
102
|
+
@connection ||= make_connection
|
103
|
+
end
|
104
|
+
|
105
|
+
def make_connection # :nodoc:
|
106
|
+
host == 'localhost' ? Rush::Connection::Local.new : Rush::Connection::Remote.new(host)
|
107
|
+
end
|
108
|
+
|
109
|
+
def ==(other) # :nodoc:
|
110
|
+
host == other.host
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# The commands module contains operations against Rush::File entries, and is
|
2
|
+
# mixed in to Rush::Entry and Array. This means you can run these commands against a single
|
3
|
+
# file, a dir full of files, or an arbitrary list of files.
|
4
|
+
#
|
5
|
+
# Examples:
|
6
|
+
#
|
7
|
+
# box['/etc/hosts'].search /localhost/ # single file
|
8
|
+
# box['/etc/'].search /localhost/ # entire directory
|
9
|
+
# box['/etc/**/*.conf'].search /localhost/ # arbitrary list
|
10
|
+
module Rush::Commands
|
11
|
+
# The entries command must return an array of Rush::Entry items. This
|
12
|
+
# varies by class that it is mixed in to.
|
13
|
+
def entries
|
14
|
+
raise "must define me in class mixed in to for command use"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Search file contents for a regular expression. A Rush::SearchResults
|
18
|
+
# object is returned.
|
19
|
+
def search(pattern)
|
20
|
+
results = Rush::SearchResults.new(pattern)
|
21
|
+
entries.each do |entry|
|
22
|
+
if !entry.dir? and matches = entry.search(pattern)
|
23
|
+
results.add(entry, matches)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
results
|
27
|
+
end
|
28
|
+
|
29
|
+
# Search and replace file contents.
|
30
|
+
def replace_contents!(pattern, with_text)
|
31
|
+
entries.each do |entry|
|
32
|
+
entry.replace_contents!(pattern, with_text) unless entry.dir?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Count the number of lines in the contained files.
|
37
|
+
def line_count
|
38
|
+
entries.inject(0) do |count, entry|
|
39
|
+
count += entry.lines.size if !entry.dir?
|
40
|
+
count
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Invoke vi on one or more files - only works locally.
|
45
|
+
def vi(*args)
|
46
|
+
names = entries.map { |f| f.quoted_path }.join(' ')
|
47
|
+
system "vim #{names} #{args.join(' ')}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# Invoke TextMate on one or more files - only works locally.
|
51
|
+
def mate(*args)
|
52
|
+
names = entries.map { |f| f.quoted_path }.join(' ')
|
53
|
+
system "mate #{names} #{args.join(' ')}"
|
54
|
+
end
|
55
|
+
end
|