crane 0.1.9
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/.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
|