rush 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +86 -0
- data/bin/rush +6 -0
- data/bin/rushd +6 -0
- data/lib/rush.rb +20 -0
- data/lib/rush/array_ext.rb +17 -0
- data/lib/rush/box.rb +63 -0
- data/lib/rush/commands.rb +55 -0
- data/lib/rush/config.rb +154 -0
- data/lib/rush/dir.rb +148 -0
- data/lib/rush/entry.rb +141 -0
- data/lib/rush/file.rb +73 -0
- data/lib/rush/fixnum_ext.rb +18 -0
- data/lib/rush/head_tail.rb +11 -0
- data/lib/rush/local.rb +224 -0
- data/lib/rush/process.rb +39 -0
- data/lib/rush/remote.rb +105 -0
- data/lib/rush/search_results.rb +58 -0
- data/lib/rush/server.rb +81 -0
- data/lib/rush/shell.rb +123 -0
- data/lib/rush/ssh_tunnel.rb +113 -0
- data/lib/rush/string_ext.rb +3 -0
- data/spec/array_ext_spec.rb +15 -0
- data/spec/base.rb +24 -0
- data/spec/box_spec.rb +18 -0
- data/spec/commands_spec.rb +47 -0
- data/spec/config_spec.rb +108 -0
- data/spec/dir_spec.rb +148 -0
- data/spec/entry_spec.rb +118 -0
- data/spec/file_spec.rb +75 -0
- data/spec/fixnum_ext_spec.rb +19 -0
- data/spec/local_spec.rb +196 -0
- data/spec/process_spec.rb +44 -0
- data/spec/remote_spec.rb +84 -0
- data/spec/search_results_spec.rb +44 -0
- data/spec/shell_spec.rb +12 -0
- data/spec/ssh_tunnel_spec.rb +106 -0
- data/spec/string_ext_spec.rb +23 -0
- metadata +91 -0
data/Rakefile
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
|
4
|
+
desc "Run all specs"
|
5
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
6
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Print specdocs"
|
10
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
11
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
12
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Run all examples with RCov"
|
16
|
+
Spec::Rake::SpecTask.new('rcov') do |t|
|
17
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
18
|
+
t.rcov = true
|
19
|
+
t.rcov_opts = ['--exclude', 'examples']
|
20
|
+
end
|
21
|
+
|
22
|
+
task :default => :spec
|
23
|
+
|
24
|
+
######################################################
|
25
|
+
|
26
|
+
require 'rake'
|
27
|
+
require 'rake/testtask'
|
28
|
+
require 'rake/clean'
|
29
|
+
require 'rake/gempackagetask'
|
30
|
+
require 'rake/rdoctask'
|
31
|
+
require 'fileutils'
|
32
|
+
include FileUtils
|
33
|
+
|
34
|
+
version = "0.1"
|
35
|
+
name = "rush"
|
36
|
+
|
37
|
+
spec = Gem::Specification.new do |s|
|
38
|
+
s.name = name
|
39
|
+
s.version = version
|
40
|
+
s.summary = "A Ruby replacement for bash+ssh."
|
41
|
+
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."
|
42
|
+
s.author = "Adam Wiggins"
|
43
|
+
s.email = "adam@heroku.com"
|
44
|
+
s.homepage = "http://rush.heroku.com/"
|
45
|
+
s.executables = [ "rush", "rushd" ]
|
46
|
+
s.rubyforge_project = "ruby-shell"
|
47
|
+
|
48
|
+
s.platform = Gem::Platform::RUBY
|
49
|
+
s.has_rdoc = true
|
50
|
+
|
51
|
+
s.files = %w(Rakefile) + Dir.glob("{bin,lib,spec}/**/*")
|
52
|
+
|
53
|
+
s.require_path = "lib"
|
54
|
+
s.bindir = "bin"
|
55
|
+
end
|
56
|
+
|
57
|
+
Rake::GemPackageTask.new(spec) do |p|
|
58
|
+
p.need_tar = true if RUBY_PLATFORM !~ /mswin/
|
59
|
+
end
|
60
|
+
|
61
|
+
task :install => [ :package ] do
|
62
|
+
sh %{sudo gem install pkg/#{name}-#{version}.gem}
|
63
|
+
end
|
64
|
+
|
65
|
+
task :uninstall => [ :clean ] do
|
66
|
+
sh %{sudo gem uninstall #{name}}
|
67
|
+
end
|
68
|
+
|
69
|
+
Rake::TestTask.new do |t|
|
70
|
+
t.libs << "spec"
|
71
|
+
t.test_files = FileList['spec/*_spec.rb']
|
72
|
+
t.verbose = true
|
73
|
+
end
|
74
|
+
|
75
|
+
Rake::RDocTask.new do |t|
|
76
|
+
t.rdoc_dir = 'doc'
|
77
|
+
t.title = "rush, a Ruby replacement for bash+ssh"
|
78
|
+
t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
79
|
+
t.options << '--charset' << 'utf-8'
|
80
|
+
t.rdoc_files.include('README')
|
81
|
+
t.rdoc_files.include('lib/rush.rb')
|
82
|
+
t.rdoc_files.include('lib/rush/*.rb')
|
83
|
+
end
|
84
|
+
|
85
|
+
CLEAN.include [ 'pkg', '*.gem', '.config' ]
|
86
|
+
|
data/bin/rush
ADDED
data/bin/rushd
ADDED
data/lib/rush.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Rush; end
|
2
|
+
module Rush::Connection; end
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/rush')
|
5
|
+
|
6
|
+
require 'config'
|
7
|
+
require 'commands'
|
8
|
+
require 'entry'
|
9
|
+
require 'file'
|
10
|
+
require 'dir'
|
11
|
+
require 'search_results'
|
12
|
+
require 'head_tail'
|
13
|
+
require 'string_ext'
|
14
|
+
require 'fixnum_ext'
|
15
|
+
require 'array_ext'
|
16
|
+
require 'process'
|
17
|
+
require 'local'
|
18
|
+
require 'remote'
|
19
|
+
require 'ssh_tunnel'
|
20
|
+
require 'box'
|
@@ -0,0 +1,17 @@
|
|
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::HeadTail
|
17
|
+
end
|
data/lib/rush/box.rb
ADDED
@@ -0,0 +1,63 @@
|
|
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('localhost')
|
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 currently running on the box. Returns an array
|
45
|
+
# of Rush::Process.
|
46
|
+
def processes
|
47
|
+
connection.processes.map do |ps|
|
48
|
+
Rush::Process.new(ps, self)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def connection # :nodoc:
|
53
|
+
@connection ||= make_connection
|
54
|
+
end
|
55
|
+
|
56
|
+
def make_connection # :nodoc:
|
57
|
+
host == 'localhost' ? Rush::Connection::Local.new : Rush::Connection::Remote.new(host)
|
58
|
+
end
|
59
|
+
|
60
|
+
def ==(other) # :nodoc:
|
61
|
+
host == other.host
|
62
|
+
end
|
63
|
+
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.full_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.full_path }.join(' ')
|
53
|
+
system "mate #{names} #{args.join(' ')}"
|
54
|
+
end
|
55
|
+
end
|
data/lib/rush/config.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
# The config class accesses files in ~/.rush to load and save user preferences.
|
2
|
+
class Rush::Config
|
3
|
+
DefaultPort = 7770
|
4
|
+
|
5
|
+
attr_reader :dir
|
6
|
+
|
7
|
+
# By default, reads from the dir ~/.rush, but an optional parameter allows
|
8
|
+
# using another location.
|
9
|
+
def initialize(location=nil)
|
10
|
+
@dir = Rush::Dir.new(location || "#{ENV['HOME']}/.rush")
|
11
|
+
@dir.create
|
12
|
+
end
|
13
|
+
|
14
|
+
# History is a flat file of past commands in the interactive shell,
|
15
|
+
# equivalent to .bash_history.
|
16
|
+
def history_file
|
17
|
+
dir['history']
|
18
|
+
end
|
19
|
+
|
20
|
+
def save_history(array)
|
21
|
+
history_file.write(array.join("\n") + "\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
def load_history
|
25
|
+
history_file.contents_or_blank.split("\n")
|
26
|
+
end
|
27
|
+
|
28
|
+
# The environment file is executed when the interactive shell starts up.
|
29
|
+
# Put aliases and your own functions here; it is the equivalent of .bashrc
|
30
|
+
# or .profile.
|
31
|
+
#
|
32
|
+
# Example ~/.rush/env.rb:
|
33
|
+
#
|
34
|
+
# server = Rush::Box.new('www@my.server')
|
35
|
+
# myproj = home['projects/myproj/']
|
36
|
+
def env_file
|
37
|
+
dir['env.rb']
|
38
|
+
end
|
39
|
+
|
40
|
+
def load_env
|
41
|
+
env_file.contents_or_blank
|
42
|
+
end
|
43
|
+
|
44
|
+
# Commands are mixed in to Array and Rush::Entry, alongside the default
|
45
|
+
# commands from Rush::Commands. Any methods here should reference "entries"
|
46
|
+
# to get the list of entries to operate on.
|
47
|
+
#
|
48
|
+
# Example ~/.rush/commands.rb:
|
49
|
+
#
|
50
|
+
# def destroy_svn(*args)
|
51
|
+
# entries.select { |e| e.name == '.svn' }.destroy
|
52
|
+
# end
|
53
|
+
def commands_file
|
54
|
+
dir['commands.rb']
|
55
|
+
end
|
56
|
+
|
57
|
+
def load_commands
|
58
|
+
commands_file.contents_or_blank
|
59
|
+
end
|
60
|
+
|
61
|
+
# Passwords contains a list of username:password combinations used for
|
62
|
+
# remote access via rushd. You can fill this in manually, or let the remote
|
63
|
+
# connection publish it automatically.
|
64
|
+
def passwords_file
|
65
|
+
dir['passwords']
|
66
|
+
end
|
67
|
+
|
68
|
+
def passwords
|
69
|
+
hash = {}
|
70
|
+
passwords_file.lines_or_empty.each do |line|
|
71
|
+
user, password = line.split(":", 2)
|
72
|
+
hash[user] = password
|
73
|
+
end
|
74
|
+
hash
|
75
|
+
end
|
76
|
+
|
77
|
+
# Credentials is the client-side equivalent of passwords. It contains only
|
78
|
+
# one username:password combination that is transmitted to the server when
|
79
|
+
# connecting. This is also autogenerated if it does not exist.
|
80
|
+
def credentials_file
|
81
|
+
dir['credentials']
|
82
|
+
end
|
83
|
+
|
84
|
+
def credentials
|
85
|
+
credentials_file.lines.first.split(":", 2)
|
86
|
+
end
|
87
|
+
|
88
|
+
def save_credentials(user, password)
|
89
|
+
credentials_file.write("#{user}:#{password}\n")
|
90
|
+
end
|
91
|
+
|
92
|
+
def credentials_user
|
93
|
+
credentials[0]
|
94
|
+
end
|
95
|
+
|
96
|
+
def credentials_password
|
97
|
+
credentials[1]
|
98
|
+
end
|
99
|
+
|
100
|
+
def ensure_credentials_exist
|
101
|
+
generate_credentials if credentials_file.contents_or_blank == ""
|
102
|
+
end
|
103
|
+
|
104
|
+
def generate_credentials
|
105
|
+
save_credentials(generate_user, generate_password)
|
106
|
+
end
|
107
|
+
|
108
|
+
def generate_user
|
109
|
+
generate_secret(4, 8)
|
110
|
+
end
|
111
|
+
|
112
|
+
def generate_password
|
113
|
+
generate_secret(8, 15)
|
114
|
+
end
|
115
|
+
|
116
|
+
def generate_secret(min, max)
|
117
|
+
chars = self.secret_characters
|
118
|
+
len = rand(max - min + 1) + min
|
119
|
+
password = ""
|
120
|
+
len.times do |index|
|
121
|
+
password += chars[rand(chars.length)]
|
122
|
+
end
|
123
|
+
password
|
124
|
+
end
|
125
|
+
|
126
|
+
def secret_characters
|
127
|
+
[ ('a'..'z'), ('1'..'9') ].inject([]) do |chars, range|
|
128
|
+
chars += range.to_a
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# ~/.rush/tunnels contains a list of previously created ssh tunnels. The
|
133
|
+
# format is host:port, where port is the local port that the tunnel is
|
134
|
+
# listening on.
|
135
|
+
def tunnels_file
|
136
|
+
dir['tunnels']
|
137
|
+
end
|
138
|
+
|
139
|
+
def tunnels
|
140
|
+
tunnels_file.lines_or_empty.inject({}) do |hash, line|
|
141
|
+
host, port = line.split(':', 2)
|
142
|
+
hash[host] = port.to_i
|
143
|
+
hash
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def save_tunnels(hash)
|
148
|
+
string = ""
|
149
|
+
hash.each do |host, port|
|
150
|
+
string += "#{host}:#{port}\n"
|
151
|
+
end
|
152
|
+
tunnels_file.write string
|
153
|
+
end
|
154
|
+
end
|
data/lib/rush/dir.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
# A dir is a subclass of Rush::Entry that contains other entries. Also known
|
2
|
+
# as a directory or a folder.
|
3
|
+
#
|
4
|
+
# Dirs can be operated on with Rush::Commands the same as an array of files.
|
5
|
+
# They also offer a square bracket accessor which can use globbing to get a
|
6
|
+
# list of files.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# dir = box['/home/adam/']
|
11
|
+
# dir['**/*.rb'].line_count
|
12
|
+
#
|
13
|
+
# In the interactive shell, dir.ls is a useful command.
|
14
|
+
class Rush::Dir < Rush::Entry
|
15
|
+
def dir?
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
def full_path
|
20
|
+
"#{super}/"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Entries contained within this dir - not recursive.
|
24
|
+
def contents
|
25
|
+
find_by_glob('*')
|
26
|
+
end
|
27
|
+
|
28
|
+
# Files contained in this dir only.
|
29
|
+
def files
|
30
|
+
contents.select { |entry| !entry.dir? }
|
31
|
+
end
|
32
|
+
|
33
|
+
# Other dirs contained in this dir only.
|
34
|
+
def dirs
|
35
|
+
contents.select { |entry| entry.dir? }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Access subentries with square brackets, e.g. dir['subdir/file']
|
39
|
+
def [](key)
|
40
|
+
key = key.to_s
|
41
|
+
if key == '**'
|
42
|
+
files_flattened
|
43
|
+
elsif key.match(/\*/)
|
44
|
+
find_by_glob(key)
|
45
|
+
else
|
46
|
+
find_by_name(key)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def find_by_name(name) # :nodoc:
|
51
|
+
Rush::Entry.factory("#{full_path}/#{name}", box)
|
52
|
+
end
|
53
|
+
|
54
|
+
def find_by_glob(glob) # :nodoc:
|
55
|
+
connection.index(full_path, glob).map do |fname|
|
56
|
+
Rush::Entry.factory("#{full_path}/#{fname}", box)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# A list of all the recursively contained entries in flat form.
|
61
|
+
def entries_tree
|
62
|
+
find_by_glob('**/*')
|
63
|
+
end
|
64
|
+
|
65
|
+
# Recursively contained files.
|
66
|
+
def files_flattened
|
67
|
+
entries_tree.select { |e| !e.dir? }
|
68
|
+
end
|
69
|
+
|
70
|
+
# Recursively contained dirs.
|
71
|
+
def dirs_flattened
|
72
|
+
entries_tree.select { |e| e.dir? }
|
73
|
+
end
|
74
|
+
|
75
|
+
# Given a list of flat filenames, product a list of entries under this dir.
|
76
|
+
# Mostly for internal use.
|
77
|
+
def make_entries(filenames)
|
78
|
+
filenames.map do |fname|
|
79
|
+
Rush::Entry.factory("#{full_path}/#{fname}")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Create a blank file within this dir.
|
84
|
+
def create_file(name)
|
85
|
+
file = self[name].create
|
86
|
+
file.write('')
|
87
|
+
file
|
88
|
+
end
|
89
|
+
|
90
|
+
# Create an empty subdir within this dir.
|
91
|
+
def create_dir(name)
|
92
|
+
name += '/' unless name.tail(1) == '/'
|
93
|
+
self[name].create
|
94
|
+
end
|
95
|
+
|
96
|
+
# Create an instantiated but not yet filesystem-created dir.
|
97
|
+
def create
|
98
|
+
connection.create_dir(full_path)
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
# Get the total disk usage of the dir and all its contents.
|
103
|
+
def size
|
104
|
+
connection.size(full_path)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Contained dirs that are not hidden.
|
108
|
+
def nonhidden_dirs
|
109
|
+
dirs.select do |dir|
|
110
|
+
!dir.hidden?
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Contained files that are not hidden.
|
115
|
+
def nonhidden_files
|
116
|
+
files.select do |file|
|
117
|
+
!file.hidden?
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Text output of dir listing, equivalent to the regular unix shell's ls command.
|
122
|
+
def ls
|
123
|
+
out = [ "#{self}" ]
|
124
|
+
nonhidden_dirs.each do |dir|
|
125
|
+
out << " #{dir.name}+"
|
126
|
+
end
|
127
|
+
nonhidden_files.each do |file|
|
128
|
+
out << " #{file.name}"
|
129
|
+
end
|
130
|
+
out.join("\n")
|
131
|
+
end
|
132
|
+
|
133
|
+
# Run rake within this dir.
|
134
|
+
def rake(*args)
|
135
|
+
system "cd #{full_path}; rake #{args.join(' ')}"
|
136
|
+
end
|
137
|
+
|
138
|
+
# Run git within this dir.
|
139
|
+
def git(*args)
|
140
|
+
system "cd #{full_path}; git #{args.join(' ')}"
|
141
|
+
end
|
142
|
+
|
143
|
+
include Rush::Commands
|
144
|
+
|
145
|
+
def entries
|
146
|
+
contents
|
147
|
+
end
|
148
|
+
end
|