sprout 0.7.219-i686-darwin10
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/TODO +12 -0
- data/bin/sprout +128 -0
- data/doc/Bundle +14 -0
- data/doc/Generator +35 -0
- data/doc/Library +63 -0
- data/doc/Task +21 -0
- data/doc/Tool +20 -0
- data/lib/platform.rb +109 -0
- data/lib/progress_bar.rb +354 -0
- data/lib/sprout.rb +496 -0
- data/lib/sprout/archive_unpacker.rb +225 -0
- data/lib/sprout/builder.rb +44 -0
- data/lib/sprout/commands/generate.rb +9 -0
- data/lib/sprout/dynamic_accessors.rb +34 -0
- data/lib/sprout/general_tasks.rb +6 -0
- data/lib/sprout/generator.rb +7 -0
- data/lib/sprout/generator/base_mixins.rb +186 -0
- data/lib/sprout/generator/named_base.rb +227 -0
- data/lib/sprout/log.rb +46 -0
- data/lib/sprout/process_runner.rb +111 -0
- data/lib/sprout/project_model.rb +278 -0
- data/lib/sprout/remote_file_loader.rb +71 -0
- data/lib/sprout/remote_file_target.rb +151 -0
- data/lib/sprout/simple_resolver.rb +88 -0
- data/lib/sprout/tasks/erb_resolver.rb +118 -0
- data/lib/sprout/tasks/gem_wrap_task.rb +200 -0
- data/lib/sprout/tasks/git_task.rb +134 -0
- data/lib/sprout/tasks/library_task.rb +118 -0
- data/lib/sprout/tasks/sftp_task.rb +248 -0
- data/lib/sprout/tasks/ssh_task.rb +153 -0
- data/lib/sprout/tasks/tool_task.rb +793 -0
- data/lib/sprout/tasks/zip_task.rb +158 -0
- data/lib/sprout/template_resolver.rb +207 -0
- data/lib/sprout/user.rb +383 -0
- data/lib/sprout/version.rb +12 -0
- data/lib/sprout/version_file.rb +89 -0
- data/lib/sprout/zip_util.rb +61 -0
- data/rakefile.rb +150 -0
- data/samples/gem_wrap/rakefile.rb +17 -0
- metadata +174 -0
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'sprout/version_file'
|
2
|
+
|
3
|
+
module Sprout
|
4
|
+
|
5
|
+
class GitTaskError < StandardError; end
|
6
|
+
|
7
|
+
# A Rake task for continous integration and automated deployment.
|
8
|
+
# This task will automatically load a +version_file+, increment
|
9
|
+
# the last digit (revision), create a new tag in Git with the full
|
10
|
+
# version number, and push tags to the remote Git repository.
|
11
|
+
#
|
12
|
+
# To use this task, simply add the following to your rakefile:
|
13
|
+
#
|
14
|
+
# desc 'Increment revision, tag and push with git'
|
15
|
+
# git :tag do |t|
|
16
|
+
# t.version_file = 'version.txt'
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
|
20
|
+
class GitTask < Rake::Task
|
21
|
+
# Path to a plain text file that contains a three-part version number.
|
22
|
+
# @see VersionFile
|
23
|
+
attr_accessor :version_file
|
24
|
+
# The remote branch to use, defaults to 'origin'.
|
25
|
+
attr_accessor :remote
|
26
|
+
# The local branch to send, defaults to 'master'.
|
27
|
+
attr_accessor :branch
|
28
|
+
# Message to use when committing after incrementing revision number.
|
29
|
+
# Defaults to 'Incremented revision number'.
|
30
|
+
attr_accessor :commit_message
|
31
|
+
|
32
|
+
def initialize(name, app)
|
33
|
+
super
|
34
|
+
@remote = 'origin'
|
35
|
+
@branch = 'master'
|
36
|
+
@commit_message = 'Incremented revision number'
|
37
|
+
end
|
38
|
+
|
39
|
+
def version
|
40
|
+
@version.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
def define
|
44
|
+
validate
|
45
|
+
@version = VersionFile.new(version_file)
|
46
|
+
end
|
47
|
+
|
48
|
+
def execute(*args)
|
49
|
+
super
|
50
|
+
# Fix numeric comparison....
|
51
|
+
while(get_tags.index(@version.to_tag)) do
|
52
|
+
@version.increment_revision
|
53
|
+
end
|
54
|
+
create_tag(@version.to_tag)
|
55
|
+
commit
|
56
|
+
push
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.define_task(args, &block)
|
60
|
+
begin
|
61
|
+
require 'git'
|
62
|
+
rescue LoadError => e
|
63
|
+
puts "You need to install the 'git' gem. Try running: sudo gem install git"
|
64
|
+
raise e
|
65
|
+
end
|
66
|
+
t = super
|
67
|
+
yield t if block_given?
|
68
|
+
t.define
|
69
|
+
return t
|
70
|
+
end
|
71
|
+
|
72
|
+
# Accessor for mocking the git gem.
|
73
|
+
def scm=(scm)
|
74
|
+
@scm = scm
|
75
|
+
end
|
76
|
+
|
77
|
+
# Will open on path if no SCM exists yet.
|
78
|
+
def scm
|
79
|
+
if(@scm.nil?)
|
80
|
+
path = path_to_git
|
81
|
+
raise GitTaskError.new("We don't appear to be inside of a git repository") if path.nil?
|
82
|
+
@scm = Git.open(path)
|
83
|
+
end
|
84
|
+
|
85
|
+
@scm
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def push
|
91
|
+
`git push #{remote} #{branch} --tags`
|
92
|
+
end
|
93
|
+
|
94
|
+
def commit
|
95
|
+
`git commit -a -m "#{commit_message}"`
|
96
|
+
end
|
97
|
+
|
98
|
+
def path_to_git
|
99
|
+
git = '.git'
|
100
|
+
parts = Dir.pwd.split(File::SEPARATOR)
|
101
|
+
|
102
|
+
while(parts.size > 0) do
|
103
|
+
path = parts.join(File::SEPARATOR)
|
104
|
+
if(File.exists?(File.join(path, git)))
|
105
|
+
return path
|
106
|
+
end
|
107
|
+
parts.pop
|
108
|
+
end
|
109
|
+
|
110
|
+
return nil
|
111
|
+
end
|
112
|
+
|
113
|
+
def get_tags
|
114
|
+
return scm.tags.collect do |t|
|
115
|
+
t.name
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def create_tag(name)
|
120
|
+
scm.add_tag name
|
121
|
+
end
|
122
|
+
|
123
|
+
def validate
|
124
|
+
raise GitTaskError.new("task.version_file is a required configuration for GitTask") if version_file.nil?
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
# Helper method for definining the git task in a rakefile
|
132
|
+
def git(args, &block)
|
133
|
+
Sprout::GitTask.define_task(args, &block)
|
134
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (c) 2007 Pattern Park
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
=end
|
23
|
+
|
24
|
+
module Sprout
|
25
|
+
class LibraryError < StandardError #:nodoc:
|
26
|
+
end
|
27
|
+
|
28
|
+
# :include: ../../../doc/Library
|
29
|
+
class LibraryTask < Rake::Task
|
30
|
+
|
31
|
+
# The RubyGems gem version string for this library (e.g., +version = '0.0.1'+)
|
32
|
+
attr_accessor :version
|
33
|
+
|
34
|
+
attr_writer :gem_name # :nodoc:
|
35
|
+
attr_writer :project_lib # :nodoc:
|
36
|
+
|
37
|
+
def self.define_task(args, &block) # :nodoc:
|
38
|
+
t = super
|
39
|
+
yield t if block_given?
|
40
|
+
t.define
|
41
|
+
end
|
42
|
+
|
43
|
+
# The full gem name like 'sprout-asunit3-library'
|
44
|
+
def gem_name
|
45
|
+
@gem_name ||= "sprout-#{clean_name}-library"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Ensure that namespaced rake tasks only use
|
49
|
+
# the final part for name-based features
|
50
|
+
def clean_name
|
51
|
+
return name.split(':').pop
|
52
|
+
end
|
53
|
+
|
54
|
+
# The path to the library source or swc that will be copied into your project.
|
55
|
+
# This can actually be any full or relative path on your system, but should almost
|
56
|
+
# always be left alone for automatic resolution.
|
57
|
+
def library_path
|
58
|
+
@library_path ||= nil
|
59
|
+
end
|
60
|
+
|
61
|
+
# Override the the project folder where the library will be installed.
|
62
|
+
#
|
63
|
+
# By default, libraries are installed into Sprout::ProjectModel +lib_dir+.
|
64
|
+
def project_lib
|
65
|
+
if(library_path.index('.swc'))
|
66
|
+
@project_lib ||= ProjectModel.instance.swc_dir
|
67
|
+
else
|
68
|
+
@project_lib ||= ProjectModel.instance.lib_dir
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Unlike other rake tasks, Library tasks are actually
|
73
|
+
# resolved at 'define' time. This allows the tool tasks
|
74
|
+
# to build their own dependencies (like file deps)
|
75
|
+
# (I'm sure there's a better way to do this, if you know how to fix this,
|
76
|
+
# and would like to contribute to sprouts, this might be a good spot for it)
|
77
|
+
def define
|
78
|
+
@file_target = sprout(gem_name, version)
|
79
|
+
@library_path = File.join(@file_target.installed_path, @file_target.archive_path)
|
80
|
+
define_file_task(library_path, project_path)
|
81
|
+
end
|
82
|
+
|
83
|
+
def execute(*args) # :nodoc:
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
# The path within the project where this library is installed
|
88
|
+
def project_path
|
89
|
+
if(File.directory?(@library_path))
|
90
|
+
# library is source dir
|
91
|
+
File.join(project_lib, clean_name)
|
92
|
+
else
|
93
|
+
# library is a binary (like swc, jar, etc)
|
94
|
+
File.join(project_lib, File.basename(@file_target.archive_path))
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def define_file_task(source, destination)
|
101
|
+
file destination do |t|
|
102
|
+
lib_path = library_path
|
103
|
+
FileUtils.makedirs(destination)
|
104
|
+
if(File.directory?(lib_path))
|
105
|
+
lib_path = "#{lib_path}/."
|
106
|
+
end
|
107
|
+
FileUtils.cp_r(lib_path, destination)
|
108
|
+
end
|
109
|
+
prerequisites << destination
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Helper method for definining and accessing LibraryTask instances in a rakefile
|
116
|
+
def library(args, &block)
|
117
|
+
Sprout::LibraryTask.define_task(args, &block)
|
118
|
+
end
|
@@ -0,0 +1,248 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (c) 2007 Pattern Park
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
This class has been commented out because the termios feature
|
24
|
+
that allows users to more securely enter passwords does not
|
25
|
+
work in a DOS shell.
|
26
|
+
|
27
|
+
It would be greatly appreciate if someone refactored this to either:
|
28
|
+
a) Get the same functionality in a cross-platform manner
|
29
|
+
b) Only require and use termios on systems that allow it
|
30
|
+
=end
|
31
|
+
|
32
|
+
#gem 'net-ssh', '1.1.4'
|
33
|
+
#gem 'net-sftp', '1.1.1'
|
34
|
+
|
35
|
+
require 'net/ssh'
|
36
|
+
require 'net/sftp'
|
37
|
+
|
38
|
+
#require 'termios'
|
39
|
+
|
40
|
+
module Sprout
|
41
|
+
class SFTPError < StandardError #:nodoc:
|
42
|
+
end
|
43
|
+
|
44
|
+
# The SFTPTask provides a simple rake task interface to the SFTP client RubyGem.
|
45
|
+
# This task can allow you to easily push build artifacts to a remote server
|
46
|
+
# with a single shell command.
|
47
|
+
class SFTPTask < Rake::Task
|
48
|
+
|
49
|
+
# Collection of files that should be transmitted to the remote server
|
50
|
+
attr_accessor :files
|
51
|
+
# Host name of the server to connect to with no protocol prefix, like: sub.yourhost.com
|
52
|
+
attr_accessor :host
|
53
|
+
# Username to send to the remote host. You will be prompted for this value if it is left null.
|
54
|
+
#
|
55
|
+
# NOTE: You should never check a file into version control with this field filled in, it is
|
56
|
+
# provided here as a convenience for getting set up.
|
57
|
+
attr_accessor :username
|
58
|
+
# The mode for transmitted files. Defaults to 0644
|
59
|
+
attr_accessor :file_mode
|
60
|
+
# the mode for created directories. Defaults to 0755
|
61
|
+
attr_accessor :dir_mode
|
62
|
+
# The local path to mask from transmitted files. This is key feature for automated file transmission.
|
63
|
+
# For example, if you are sending a file:
|
64
|
+
# bin/assets/img/SomeImage.jpg
|
65
|
+
# into a directory on your server like:
|
66
|
+
# /var/www/someproject/
|
67
|
+
# You don't necessarily want the 'bin' folder copied over, so you set the local_path to 'bin' like:
|
68
|
+
# t.local_path = 'bin'
|
69
|
+
# and your server will have the file uploaded to:
|
70
|
+
# /var/www/someproject/assets/img/SomeImage.jpg
|
71
|
+
attr_accessor :local_path
|
72
|
+
# The Remote base path where files should be transmitted, can be absolute or relative.
|
73
|
+
attr_accessor :remote_path
|
74
|
+
# Password to send to the remote host. You will be prompted for this value if it is left null.
|
75
|
+
#
|
76
|
+
# NOTE: You should never check a file into version control with this field filled in, it is
|
77
|
+
# provided here as a convenience for getting set up.
|
78
|
+
attr_accessor :password
|
79
|
+
|
80
|
+
def initialize(task_name, app)
|
81
|
+
super(task_name, app)
|
82
|
+
@name = name
|
83
|
+
@files = []
|
84
|
+
@dir_mode = 0755
|
85
|
+
@file_mode = 0644
|
86
|
+
@host = nil
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.define_task(args, &block) # :nodoc:
|
90
|
+
t = super
|
91
|
+
yield t if block_given?
|
92
|
+
end
|
93
|
+
|
94
|
+
def execute(*args) # :nodoc:
|
95
|
+
if(@files.size == 0)
|
96
|
+
if(@local_path)
|
97
|
+
expr = @local_path + '/**/**/*'
|
98
|
+
@files = FileList[expr]
|
99
|
+
else
|
100
|
+
raise SFTPError.new('SFTP requires either a local_path or files to be transmitted')
|
101
|
+
end
|
102
|
+
else
|
103
|
+
if(!@local_path)
|
104
|
+
@local_path = ''
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
if(@host.nil?)
|
109
|
+
raise SFTPError.new('SFTP requires non-nil host parameter')
|
110
|
+
end
|
111
|
+
|
112
|
+
if(@username.nil?)
|
113
|
+
print "Username: "
|
114
|
+
@username = $stdin.gets.chomp!
|
115
|
+
raise SFTPError.new('SFTP requires username parameter') unless @username
|
116
|
+
end
|
117
|
+
|
118
|
+
if(@password.nil?)
|
119
|
+
print "Password: "
|
120
|
+
@password = $stdin.gets.chomp!
|
121
|
+
# @password = Password.get
|
122
|
+
raise SFTPError.new('SFTP requires password parameter') unless @password
|
123
|
+
end
|
124
|
+
|
125
|
+
if(get_confirmation)
|
126
|
+
puts ">> Connecting to Remote Server: #{@username}@#{@host}:#{@remote_path}"
|
127
|
+
|
128
|
+
Net::SFTP.start(@host, @username, @password) do |sftp|
|
129
|
+
begin
|
130
|
+
dir = sftp.opendir(@remote_path)
|
131
|
+
rescue Net::SFTP::Operations::StatusException
|
132
|
+
puts "[ERROR] [#{@remote_path}] does not exist on the server"
|
133
|
+
return
|
134
|
+
end
|
135
|
+
for file in @files
|
136
|
+
next if File.stat(file).directory?
|
137
|
+
remote_file = remote_file_name(file)
|
138
|
+
put_file(file, remote_file, sftp)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
else
|
142
|
+
puts "[WARNING] Publish aborted by user request"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def put_file(local_file, remote_file, sftp) # :nodoc:
|
147
|
+
begin
|
148
|
+
create_remote_dir(remote_file, sftp)
|
149
|
+
|
150
|
+
if(file_changed(local_file, remote_file, sftp))
|
151
|
+
puts ">> Pushing #{local_file} to: #{remote_file}"
|
152
|
+
sftp.put_file(local_file, remote_file)
|
153
|
+
end
|
154
|
+
rescue Net::SFTP::Operations::StatusException => e
|
155
|
+
raise unless e.code == 2
|
156
|
+
sftp.put_file(local_file, remote_file)
|
157
|
+
sftp.setstat(remote_file, :permissions => @file_mode)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def create_remote_dir(path, sftp) # :nodoc:
|
162
|
+
begin
|
163
|
+
sftp.stat(File.dirname(path))
|
164
|
+
rescue Net::SFTP::Operations::StatusException => e
|
165
|
+
raise unless e.code == 2
|
166
|
+
dir = File.dirname(path.sub(@remote_path, ''))
|
167
|
+
parts = dir.split(File::SEPARATOR)
|
168
|
+
part = File.join(@remote_path, parts.shift)
|
169
|
+
while(part)
|
170
|
+
begin
|
171
|
+
sftp.stat(part)
|
172
|
+
rescue Net::SFTP::Operations::StatusException => ne
|
173
|
+
raise unless ne.code == 2
|
174
|
+
sftp.mkdir(part, :permissions => @dir_mode)
|
175
|
+
end
|
176
|
+
if(parts.size > 0)
|
177
|
+
part = File.join(part, parts.shift)
|
178
|
+
else
|
179
|
+
part = nil
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def file_changed(local_file, remote_file, sftp) # :nodoc:
|
186
|
+
local_stat = File.stat(local_file)
|
187
|
+
remote_stat = sftp.stat(remote_file)
|
188
|
+
time_difference = (local_stat.mtime > Time.at(remote_stat.mtime))
|
189
|
+
size_difference = (local_stat.size != remote_stat.size)
|
190
|
+
return (time_difference || size_difference)
|
191
|
+
end
|
192
|
+
|
193
|
+
def remote_file_name(file) # :nodoc:
|
194
|
+
return @remote_path + file.sub(@local_path, '')
|
195
|
+
end
|
196
|
+
|
197
|
+
def get_confirmation # :nodoc:
|
198
|
+
puts "-----------------------------------------"
|
199
|
+
puts "Are you sure you want to publish #{@files.size} files to:"
|
200
|
+
puts "#{@username}@#{@host}:#{@remote_path}? [Yn]"
|
201
|
+
response = $stdin.gets.chomp!
|
202
|
+
return (response.downcase == 'y' || response == "")
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
=begin
|
208
|
+
# The following implementation does not work at all on DOS machines,
|
209
|
+
# is there a cross-platform way to solve the secure password prompt problem?
|
210
|
+
# Password handling snippet found at: http://www.caliban.org/ruby/ruby-password.shtml
|
211
|
+
class Password
|
212
|
+
|
213
|
+
def Password.get(message="Password: ")
|
214
|
+
begin
|
215
|
+
if $stdin.tty?
|
216
|
+
Password.echo false
|
217
|
+
print message if message
|
218
|
+
end
|
219
|
+
|
220
|
+
return $stdin.gets.chomp
|
221
|
+
ensure
|
222
|
+
if $stdin.tty?
|
223
|
+
Password.echo true
|
224
|
+
print "\n"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def Password.echo(on=true, masked=false)
|
230
|
+
term = Termios::getattr( $stdin )
|
231
|
+
|
232
|
+
if on
|
233
|
+
term.c_lflag |= ( Termios::ECHO | Termios::ICANON )
|
234
|
+
else # off
|
235
|
+
term.c_lflag &= ~Termios::ECHO
|
236
|
+
term.c_lflag &= ~Termios::ICANON if masked
|
237
|
+
end
|
238
|
+
|
239
|
+
Termios::setattr( $stdin, Termios::TCSANOW, term )
|
240
|
+
end
|
241
|
+
end
|
242
|
+
=end
|
243
|
+
end
|
244
|
+
|
245
|
+
|
246
|
+
def sftp(args, &block)
|
247
|
+
Sprout::SFTPTask.define_task(args, &block)
|
248
|
+
end
|