rush 0.1
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/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
|