crane 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +11 -0
- data/Gemfile +4 -0
- data/README.markdown +51 -0
- data/Rakefile +9 -0
- data/bin/crane +6 -0
- data/crane.gemspec +24 -0
- data/lib/crane.rb +3 -0
- data/lib/crane/commands/init.rb +42 -0
- data/lib/crane/commands/push.rb +151 -0
- data/lib/crane/commands/test.rb +59 -0
- data/lib/crane/config.rb +110 -0
- data/lib/crane/crane.rb +118 -0
- data/lib/crane/ftp.rb +142 -0
- data/lib/crane/shell_initializer.rb +55 -0
- data/lib/crane/version.rb +3 -0
- data/lib/shell/shell.rb +125 -0
- data/test/bin/test_crane.rb +22 -0
- data/test/lib/crane/commands/test_push.rb +46 -0
- data/test/lib/crane/commands/test_test.rb +37 -0
- data/test/lib/crane/test_config.rb +107 -0
- data/test/lib/crane/test_ftp.rb +68 -0
- data/test/lib/crane/test_shell_initializer.rb +52 -0
- data/test/lib/shell/test_parser.rb +26 -0
- data/test/resources/configurations/crane +5 -0
- data/test/resources/configurations/crane_wrong_remote_dir +5 -0
- data/test/resources/source_codes/today_file.rb +0 -0
- data/test/set_test_environment.rb +1 -0
- metadata +97 -0
data/lib/crane/crane.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
require "net/ftp"
|
2
|
+
require "crane/config"
|
3
|
+
require File.expand_path("../../shell/shell.rb", __FILE__)
|
4
|
+
|
5
|
+
module Crane
|
6
|
+
|
7
|
+
class Engine < Shell::Run
|
8
|
+
|
9
|
+
include Config
|
10
|
+
# @argv
|
11
|
+
# @command
|
12
|
+
# options
|
13
|
+
|
14
|
+
def initialize argv = []
|
15
|
+
return true if defined? TESTING
|
16
|
+
require File.expand_path("../ftp.rb", __FILE__)
|
17
|
+
|
18
|
+
super argv
|
19
|
+
@ftp = nil
|
20
|
+
@config = Config.load_config
|
21
|
+
@ignored_files = get_ignored_files
|
22
|
+
@local_files = []
|
23
|
+
|
24
|
+
@errors = []
|
25
|
+
@connection = nil
|
26
|
+
|
27
|
+
(help; exit) if ["help", "h"].any? { |e| true if Shell::Parser.get_options(@argv).include? e }
|
28
|
+
(version; exit) if ["version", "v"].any? { |e| true if Shell::Parser.get_options(@argv).include? e }
|
29
|
+
|
30
|
+
begin
|
31
|
+
run unless defined? TESTING
|
32
|
+
rescue Interrupt
|
33
|
+
puts "\n"
|
34
|
+
rescue
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# return an instance of a FTP connection
|
40
|
+
def connect_ftp
|
41
|
+
require "net/ftp"
|
42
|
+
require 'timeout'
|
43
|
+
result = true
|
44
|
+
|
45
|
+
if @connection.nil? then
|
46
|
+
host = @config['ftp']['host_address']
|
47
|
+
username = @config['ftp']['username']
|
48
|
+
password = @config['ftp']['password']
|
49
|
+
@connection = Net::FTP.new
|
50
|
+
@connection.passive = true
|
51
|
+
begin
|
52
|
+
Timeout.timeout(7) do
|
53
|
+
@connection.connect host, 21
|
54
|
+
end
|
55
|
+
rescue
|
56
|
+
@connection_error = "couldn't connect to host"
|
57
|
+
result = false
|
58
|
+
end
|
59
|
+
|
60
|
+
begin
|
61
|
+
Timeout.timeout(4) do
|
62
|
+
@connection.login username, password
|
63
|
+
end
|
64
|
+
rescue
|
65
|
+
@connection_error = "invalid username/password"
|
66
|
+
result = false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
if result == false
|
71
|
+
@connection = false
|
72
|
+
end
|
73
|
+
|
74
|
+
@connection
|
75
|
+
|
76
|
+
end # connect_ftp
|
77
|
+
|
78
|
+
# if there was typed any argument on ARGV starting with either -- or -
|
79
|
+
def is_option option
|
80
|
+
Shell::Parser.is_option option, @argv
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_ignored_files
|
84
|
+
require File.expand_path("../config.rb", __FILE__);
|
85
|
+
Config.get_ignored_files || []
|
86
|
+
end
|
87
|
+
|
88
|
+
def no_command
|
89
|
+
help
|
90
|
+
end
|
91
|
+
|
92
|
+
def version
|
93
|
+
require "crane/version"
|
94
|
+
print "Crane v" + Crane::VERSION + "\n"
|
95
|
+
end
|
96
|
+
|
97
|
+
def help
|
98
|
+
print "Usage:\n"
|
99
|
+
print "\s\scrane COMMAND [options]"
|
100
|
+
print "\n\n"
|
101
|
+
print "Commands available:"
|
102
|
+
print "\n"
|
103
|
+
print "\s\spush\t\tSend files to a remote server"
|
104
|
+
print "\n\n"
|
105
|
+
print "Examples:"
|
106
|
+
print "\n"
|
107
|
+
print "\s\scrane push 1h"
|
108
|
+
print "\n"
|
109
|
+
print "\t\tPushes all files modified in the last hour.\n"
|
110
|
+
print "\n"
|
111
|
+
print "For more information, try:\n"
|
112
|
+
print "\s\scrane COMMAND -h"
|
113
|
+
print "\n"
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
data/lib/crane/ftp.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
require "net/ftp"
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
module Crane
|
5
|
+
|
6
|
+
class Ftp
|
7
|
+
|
8
|
+
attr_accessor :connection, :connection_error
|
9
|
+
|
10
|
+
def initialize c = false
|
11
|
+
@connection = nil
|
12
|
+
@existent_dirs = []
|
13
|
+
@nlst = {}
|
14
|
+
unless c
|
15
|
+
config = Config.load_config
|
16
|
+
unless config.empty?
|
17
|
+
c = {}
|
18
|
+
c[:ftp] = config[:ftp] unless config[:ftp].empty?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
connect(c) if c
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def connect c = {}
|
26
|
+
port = c[:port] || 21
|
27
|
+
c = c[:ftp] if c.include? :ftp
|
28
|
+
|
29
|
+
return false unless c.has_key? :host
|
30
|
+
|
31
|
+
@connection = Net::FTP.new
|
32
|
+
@connection_error = false
|
33
|
+
@connection.passive = true
|
34
|
+
begin
|
35
|
+
Timeout.timeout(10) do
|
36
|
+
@connection.connect c[:host], port
|
37
|
+
end
|
38
|
+
rescue Timeout::Error
|
39
|
+
@connection_error = "Timeout on connection"
|
40
|
+
rescue
|
41
|
+
@connection_error = "Error on connection FTP"
|
42
|
+
end
|
43
|
+
|
44
|
+
begin
|
45
|
+
Timeout.timeout(20) do
|
46
|
+
@connection.login c[:username], c[:password]
|
47
|
+
end
|
48
|
+
rescue Timeout::Error
|
49
|
+
@connection_error = "Timeout on authentication (20 seconds)"
|
50
|
+
rescue Net::FTPPermError
|
51
|
+
@connection_error = "Invalid username/password"
|
52
|
+
rescue
|
53
|
+
@connection_error = "Unknown error related to username/password"
|
54
|
+
end
|
55
|
+
|
56
|
+
return false if @connection_error
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
def remote_dir_exists? remote_dir = false
|
61
|
+
return nil if Config.load_config.empty?
|
62
|
+
connect(Config.CONFIG[:ftp]) if @connection.nil?
|
63
|
+
|
64
|
+
begin
|
65
|
+
Timeout.timeout(10) do
|
66
|
+
@connection.chdir(remote_dir || Config.CONFIG[:ftp][:remote_root_dir])
|
67
|
+
end
|
68
|
+
rescue
|
69
|
+
return false
|
70
|
+
end
|
71
|
+
@connection.close
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
75
|
+
def close
|
76
|
+
@connection.close
|
77
|
+
end
|
78
|
+
|
79
|
+
def putbinaryfile f, f2
|
80
|
+
@connection.putbinaryfile f, f2
|
81
|
+
end
|
82
|
+
|
83
|
+
def chdir var
|
84
|
+
@connection.chdir var
|
85
|
+
end
|
86
|
+
|
87
|
+
# Accepts deep directories as parameter
|
88
|
+
def mkdir dir
|
89
|
+
each_dir = dir.split("/")
|
90
|
+
return false if dir.empty?
|
91
|
+
last_dir = ""
|
92
|
+
|
93
|
+
each_dir.each { |d|
|
94
|
+
next if ["", ".", ".."].include? d
|
95
|
+
|
96
|
+
slashed_d = "/" + d if d[0,1] != "/"
|
97
|
+
current_dir = last_dir + slashed_d
|
98
|
+
|
99
|
+
if @existent_dirs.include? current_dir
|
100
|
+
last_dir << slashed_d
|
101
|
+
next
|
102
|
+
else
|
103
|
+
@connection.chdir last_dir unless last_dir.empty?
|
104
|
+
|
105
|
+
if @nlst.include? last_dir
|
106
|
+
nlst = @nlst[last_dir]
|
107
|
+
else
|
108
|
+
nlst = @connection.nlst
|
109
|
+
end
|
110
|
+
@nlst[last_dir] = nlst
|
111
|
+
if nlst.include? d
|
112
|
+
@existent_dirs << current_dir
|
113
|
+
last_dir << slashed_d
|
114
|
+
next
|
115
|
+
else
|
116
|
+
@connection.mkdir d
|
117
|
+
@connection.chdir current_dir
|
118
|
+
last_dir << slashed_d
|
119
|
+
@existent_dirs << current_dir
|
120
|
+
end
|
121
|
+
end
|
122
|
+
}
|
123
|
+
@connection.chdir pwd
|
124
|
+
end
|
125
|
+
|
126
|
+
def pwd
|
127
|
+
@connection.pwd
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
def map_args args
|
132
|
+
result = {}
|
133
|
+
args.each { |k,v|
|
134
|
+
result[k] = v
|
135
|
+
}
|
136
|
+
result
|
137
|
+
end #map_args
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "shell/shell"
|
2
|
+
require "crane"
|
3
|
+
require "crane/crane"
|
4
|
+
|
5
|
+
module Shell
|
6
|
+
class Initializer
|
7
|
+
|
8
|
+
attr_accessor :command
|
9
|
+
|
10
|
+
def initialize argv
|
11
|
+
@args = argv
|
12
|
+
get_command
|
13
|
+
run
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
if @command
|
18
|
+
if command_exist?
|
19
|
+
require get_command_file
|
20
|
+
return run_command @command
|
21
|
+
end
|
22
|
+
end
|
23
|
+
inexistent_command
|
24
|
+
end
|
25
|
+
|
26
|
+
def should_exit?
|
27
|
+
return false || true unless @command
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_command
|
31
|
+
@command = Shell::Parser::get_command @args
|
32
|
+
end
|
33
|
+
|
34
|
+
def command_exist? command = false
|
35
|
+
File.exists? get_command_file(command)
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_command_file command = false
|
39
|
+
command = @command unless command
|
40
|
+
File.expand_path("../commands/" + command + ".rb", __FILE__)
|
41
|
+
end
|
42
|
+
|
43
|
+
def run_command command, args = []
|
44
|
+
command = @command unless command
|
45
|
+
return inexistent_command unless command_exist?(command)
|
46
|
+
require get_command_file(command)
|
47
|
+
command = command.capitalize
|
48
|
+
@command_obj = Crane::Commands.const_get(command).new(@args)
|
49
|
+
end
|
50
|
+
|
51
|
+
def inexistent_command
|
52
|
+
@command_obj = Crane::Engine.new(@args)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/shell/shell.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
module Shell
|
2
|
+
|
3
|
+
class Run
|
4
|
+
|
5
|
+
# all arguments passed
|
6
|
+
@argv
|
7
|
+
|
8
|
+
def initialize argv
|
9
|
+
@argv = argv
|
10
|
+
|
11
|
+
@options = Shell::Parser.get_options @argv
|
12
|
+
@command = Shell::Parser.get_command @argv
|
13
|
+
@arguments = Shell::Parser.get_arguments @argv
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
class Input
|
19
|
+
|
20
|
+
def self.text
|
21
|
+
STDOUT.flush
|
22
|
+
STDIN.gets.strip
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.yesno message
|
26
|
+
STDOUT.flush
|
27
|
+
has_result = false
|
28
|
+
while has_result == false
|
29
|
+
print message.strip + " "
|
30
|
+
input = STDIN.gets.strip
|
31
|
+
|
32
|
+
if input == "yes" or input == "y" then
|
33
|
+
final_input = "yes"
|
34
|
+
elsif input == "no" or input == "n" then
|
35
|
+
final_input = "no"
|
36
|
+
end
|
37
|
+
|
38
|
+
if ( final_input == "yes" or final_input == "no" )
|
39
|
+
has_result = true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
if final_input == "yes"
|
44
|
+
result = true
|
45
|
+
else
|
46
|
+
result = false
|
47
|
+
end
|
48
|
+
result
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
class Parser
|
54
|
+
|
55
|
+
# ARGV has a command, options and arguments. The design is:
|
56
|
+
#
|
57
|
+
# $ bin_file command argument1 argument2 -option1 -option2
|
58
|
+
#
|
59
|
+
# get_options() and get_arguments() return Array. get_command()
|
60
|
+
# returns String.
|
61
|
+
|
62
|
+
def self.get_command argv = []
|
63
|
+
command = String.new
|
64
|
+
|
65
|
+
argv.each {
|
66
|
+
|e|
|
67
|
+
e_length = e.length
|
68
|
+
if (e[0,2] != "--" and e[0,1] != "-") then
|
69
|
+
command = e
|
70
|
+
break
|
71
|
+
end
|
72
|
+
}
|
73
|
+
return false if command.empty?
|
74
|
+
command
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.get_options argv
|
78
|
+
return [] if argv.nil?
|
79
|
+
@options = []
|
80
|
+
@sanitized_options = []
|
81
|
+
|
82
|
+
argv.each {
|
83
|
+
|e|
|
84
|
+
e_length = e.length
|
85
|
+
if e[0,2] == "--"
|
86
|
+
@options.push e[2,e_length]
|
87
|
+
elsif e[0,1] == "-"
|
88
|
+
@options.push e[1,e_length]
|
89
|
+
end
|
90
|
+
}
|
91
|
+
unless @options.empty?
|
92
|
+
@options.each { |e|
|
93
|
+
next if @sanitized_options.include?(e)
|
94
|
+
@sanitized_options << e
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
@sanitized_options
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.get_arguments argv
|
102
|
+
@arguments = []
|
103
|
+
i = 0
|
104
|
+
argv.each {
|
105
|
+
|e|
|
106
|
+
|
107
|
+
i+= 1
|
108
|
+
next if i == 1
|
109
|
+
|
110
|
+
e_length = e.length
|
111
|
+
if e[0,2] != "--" and e[0,1] != "-"
|
112
|
+
@arguments.push e[0,e_length]
|
113
|
+
end
|
114
|
+
}
|
115
|
+
@arguments
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.is_option option, argv = []
|
119
|
+
argv_options = self.get_options argv
|
120
|
+
argv_options.include?(option)
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|