girror 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.cvsignore ADDED
@@ -0,0 +1,3 @@
1
+ .git
2
+ .document
3
+ .gitignore
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "shoulda", ">= 0"
10
+ gem "bundler", "~> 1.0.0"
11
+ gem "jeweler", "~> 1.5.1"
12
+ gem "rcov", ">= 0"
13
+ end
14
+
15
+ gem "net-sftp"
16
+ gem "git"
17
+ gem "highline"
18
+ gem "syslog-logger"
data/Gemfile.lock ADDED
@@ -0,0 +1,30 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ highline (1.6.1)
6
+ jeweler (1.5.1)
7
+ bundler (~> 1.0.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ net-sftp (2.0.5)
11
+ net-ssh (>= 2.0.9)
12
+ net-ssh (2.0.23)
13
+ rake (0.8.7)
14
+ rcov (0.9.9)
15
+ shoulda (2.11.3)
16
+ syslog-logger (1.6.4)
17
+
18
+ PLATFORMS
19
+ ruby
20
+ x86-mingw32
21
+
22
+ DEPENDENCIES
23
+ bundler (~> 1.0.0)
24
+ git
25
+ highline
26
+ jeweler (~> 1.5.1)
27
+ net-sftp
28
+ rcov
29
+ shoulda
30
+ syslog-logger
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ == MIT license
2
+
3
+ Copyright (c) 2010-2011 Pavel Argentov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,123 @@
1
+ = girror
2
+
3
+ Girror == [G]it the M[irror]
4
+
5
+ A CLI remote-to-local directory mirroring tool, the poor man's "filezilla" which
6
+ I couldn't google up, so decided to make it myself.
7
+
8
+ What does it do?
9
+
10
+ - Logs into the remote sftp server;
11
+ - Changes to the dir indicated in the CLI parameter;
12
+ - Gets the directory tree from there;
13
+ - Compares the tree to the local one;
14
+ - Downloads all the files/directories which are:
15
+ - don't exist in the local tree,
16
+ - newer than local ones (mtime comparison),
17
+ - have the mode/owner changed (this feature is available only if the local
18
+ system is Unix);
19
+ - Removes all the removed-on-remote files;
20
+ - Wraps all the operations into git transactions (creation/deletion
21
+ of files/dirs);
22
+ - Commits the changes to the local git repository.
23
+
24
+ Girror doesn't need any special server software on the remote side
25
+ besides the ssh/sftp.
26
+
27
+ == CLI reference
28
+
29
+ === girror -h
30
+ Girror cli tool, version 0.0.1.
31
+
32
+ Mirrors the remote sftp site and stores the changes in a local git repo.
33
+
34
+ Command Line Usage:
35
+
36
+ girror [options] remote_uri
37
+
38
+ remote_uri: a complete sftp uri of a remote location;
39
+
40
+ remote_uri may be in the form of either "user:pass@host:path" or
41
+ "host:path". In the latter case username is taken from the environment
42
+ variable $USERNAME.
43
+
44
+ Options:
45
+ -l, --log place place = ['syslog' or a filename]: Logging destination (default is STDERR).
46
+ --renc encoding Remote filename encoding; defaults to 'utf-8'.
47
+ --lenc encoding Local filename encoding; defaults to 'utf-8'.
48
+ -o, --output path Output directory, should be a git repo workdir.
49
+ -v, --verbose Be verbose: log tons of debugging. THOUSANDS OF THEM!
50
+ -h, --help Display this help message.
51
+
52
+ == Configuration file
53
+
54
+ Some runtime options may be specified in _girror/config.rb file in local "mirror" directory.
55
+ Here's the example with the tested options:
56
+
57
+ === _girror/config.rb
58
+ #
59
+ # girror config for current mirror
60
+ #
61
+ module Config
62
+
63
+ # Program options for the current instance
64
+ OPTIONS = {
65
+ # custom commit message
66
+ :commit_msg => Proc.new {"State at #{Time.now}"},
67
+
68
+ # local filename encoding
69
+ :lenc => 'cp1251',
70
+
71
+ # remote filename encoding
72
+ :renc => 'koi8-r'
73
+ }
74
+
75
+ end
76
+
77
+ == Usage example
78
+
79
+ [paul@paul site]$ git init mirror
80
+ Initialized empty Git repository in /usr/home/paul/work/Krotov/site/mirror/.git/
81
+ [paul@paul site]$ girror -o mirror paul@paul:/home/paul/develop/ruby/girror
82
+ I, [2010-12-31T13:43:35.843256 #2341] INFO -- : Starting
83
+ I, [2010-12-31T13:43:35.843515 #2341] INFO -- : Opening local git repo at mirror
84
+ I, [2010-12-31T13:43:35.843848 #2341] INFO -- : Changed to /usr/home/paul/work/Krotov/site/mirror
85
+ I, [2010-12-31T13:43:35.860511 #2341] INFO -- : Not using stored config: no such file to load -- config
86
+ Enter passphrase for /home/paul/.ssh/id_dsa:
87
+ I, [2010-12-31T13:43:40.856664 #2341] INFO -- : Connected to remote paul as paul
88
+ I, [2010-12-31T13:43:40.863612 #2341] INFO -- : Fetching file /home/paul/develop/ruby/girror/Rakefile -> ./Rakefile (1565 bytes)
89
+ I, [2010-12-31T13:43:40.866610 #2341] INFO -- : Setting mode: ./Rakefile => 100644
90
+ I, [2010-12-31T13:43:40.866853 #2341] INFO -- : Setting mtime: ./Rakefile => ["2010-12-31 13:39:07", "2010-12-31 13:28:11"]
91
+ I, [2010-12-31T13:43:40.882452 #2341] INFO -- : Fetching file /home/paul/develop/ruby/girror/Gemfile -> ./Gemfile (423 bytes)
92
+ I, [2010-12-31T13:43:40.885195 #2341] INFO -- : Setting mode: ./Gemfile => 100644
93
+ I, [2010-12-31T13:43:40.885317 #2341] INFO -- : Setting mtime: ./Gemfile => ["2010-12-31 13:39:08", "2010-12-28 16:26:55"]
94
+
95
+ ...
96
+
97
+ I, [2010-12-31T13:43:40.974504 #2341] INFO -- : Fetching directory /home/paul/develop/ruby/girror/pkg -> ./pkg | [2010-12-31 13:39:07 +0300, 2010-12-31 13:39:08 +0300, 0, 0, "40755"]
98
+ I, [2010-12-31T13:43:40.980814 #2341] INFO -- : Fetching file /home/paul/develop/ruby/girror/pkg/girror-0.0.0.gem -> ./pkg/girror-0.0.0.gem (61440 bytes)
99
+ I, [2010-12-31T13:43:40.995837 #2341] INFO -- : Setting mode: ./pkg/girror-0.0.0.gem => 100644
100
+ I, [2010-12-31T13:43:40.996319 #2341] INFO -- : Setting mtime: ./pkg/girror-0.0.0.gem => ["2010-12-31 13:39:10", "2010-12-31 13:39:07"]
101
+ I, [2010-12-31T13:43:40.996829 #2341] INFO -- : Setting mode: ./pkg => 40755
102
+ I, [2010-12-31T13:43:40.997039 #2341] INFO -- : Setting mtime: ./pkg => ["2010-12-31 13:39:08", "2010-12-31 13:39:07"]
103
+ I, [2010-12-31T13:43:40.997403 #2341] INFO -- : Disconnected from remote paul
104
+ I, [2010-12-31T13:43:40.997466 #2341] INFO -- : Committing changes to local git repo
105
+ I, [2010-12-31T13:43:41.061205 #2341] INFO -- : Finishing
106
+
107
+ Note that remote git-related files aren't got.
108
+
109
+ == Contributing to girror
110
+
111
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
112
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
113
+ * Fork the project
114
+ * Start a feature/bugfix branch
115
+ * Commit and push until you are happy with your contribution
116
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
117
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
118
+
119
+ == Copyright
120
+
121
+ Copyright (c) 2010 Pavel Argentov. See LICENSE.txt for
122
+ further details.
123
+
data/Rakefile ADDED
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+ require 'rubygems'
3
+ require 'bundler'
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+ require 'rake'
12
+
13
+ require 'jeweler'
14
+ Jeweler::Tasks.new do |gem|
15
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
16
+ gem.name = "girror"
17
+ gem.homepage = "http://github.com/argent-smith/girror"
18
+ gem.license = "MIT"
19
+ gem.summary = %Q{Remote -> local directory 'mirror' using SFTP transport and Git storage.}
20
+ gem.description = %Q{Retrieves remote directory via SFTP and stores it in local Git repository.}
21
+ gem.email = "argentoff@gmail.com"
22
+ gem.authors = ["Pavel Argentov"]
23
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
24
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
25
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
26
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
27
+ gem.add_runtime_dependency 'net-sftp'
28
+ gem.add_runtime_dependency 'git'
29
+ gem.add_runtime_dependency 'highline'
30
+ gem.add_runtime_dependency 'syslog-logger'
31
+ end
32
+ Jeweler::RubygemsDotOrgTasks.new
33
+
34
+ require 'rake/rdoctask'
35
+ Rake::RDocTask.new do |rdoc|
36
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
37
+
38
+ rdoc.rdoc_dir = 'rdoc'
39
+ rdoc.title = "girror #{version}"
40
+ rdoc.rdoc_files.include('README*')
41
+ rdoc.rdoc_files.include('LICENSE*')
42
+ rdoc.rdoc_files.include('lib/**/*.rb')
43
+ end
data/TODO.md ADDED
@@ -0,0 +1,8 @@
1
+ Project todos
2
+ =============
3
+
4
+ TODO:
5
+
6
+ - Documentation
7
+ - Multithreaded download
8
+ - Traffic shaping
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/bin/girror ADDED
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # -*- Ruby -*-
4
+ #
5
+ # Author:: Pavel Argentov <argentoff@gmail.com>
6
+ #
7
+ # Mirror the remote sftp site and store the changes in a local git repo.
8
+
9
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
10
+
11
+ require 'optparse'
12
+ require 'girror'
13
+
14
+ help = <<HELP
15
+ Girror cli tool, version #{Girror::VERSION}.
16
+
17
+ Mirrors the remote sftp site and stores the changes in a local git repo.
18
+
19
+ Command Line Usage:
20
+
21
+ girror [options] remote_uri
22
+
23
+ remote_uri: a complete sftp uri of a remote location;
24
+
25
+ remote_uri may be in the form of either "user:pass@host:path" or
26
+ "host:path". In the latter case username is taken from the environment
27
+ variable $USERNAME.
28
+
29
+ Options:
30
+ HELP
31
+
32
+ options = {}
33
+ opts = OptionParser.new do |opts|
34
+ opts.banner = help
35
+
36
+ opts.on("-l place", "--log place", "place = ['syslog' or a filename]: Logging destination (default is STDERR).") do |log|
37
+ options[:log] = log
38
+ end
39
+
40
+ opts.on("--renc encoding", "Remote filename encoding; defaults to 'utf-8'.") do |enc|
41
+ options[:renc] = enc
42
+ end
43
+
44
+ opts.on("--lenc encoding", "Local filename encoding; defaults to 'utf-8'.") do |enc|
45
+ options[:lenc] = enc
46
+ end
47
+
48
+ opts.on("-o path", "--output path", "Output directory, should be a git repo workdir.") do |path|
49
+ options[:to] = path
50
+ end
51
+
52
+ # opts.on("--dr", "Dry run: only logs what's to be done") do |addr|
53
+ # options[:dr] = true
54
+ # end
55
+
56
+ opts.on("-v", "--verbose", "Be verbose: log tons of debugging. THOUSANDS OF THEM!") do
57
+ options[:verbose] = true
58
+ end
59
+
60
+ opts.on("-h", "--help", "Display this help message.") do
61
+ puts opts.help
62
+ exit 1
63
+ end
64
+ end
65
+
66
+ # Read command line options into `options` hash
67
+ opts.parse!
68
+
69
+ case ARGV.size
70
+ when 1
71
+ options[:from] = ARGV[0]
72
+ options[:to] = "." if options[:to].nil?
73
+ else
74
+ puts "Incorrect arguments; use -h to see help."
75
+ exit 1
76
+ end
77
+
78
+ Girror::Application.run options
@@ -0,0 +1,18 @@
1
+ #
2
+ # girror config for current mirror
3
+ #
4
+ module Config
5
+
6
+ # Program options for the current instance
7
+ OPTIONS = {
8
+ # custom commit message
9
+ :commit_msg => Proc.new {"State at #{Time.now}"},
10
+
11
+ # local filename encoding
12
+ :lenc => 'cp1251',
13
+
14
+ # remote filename encoding
15
+ :renc => 'koi8-r'
16
+ }
17
+
18
+ end
data/gem_graph.png ADDED
Binary file
data/girror.gemspec ADDED
@@ -0,0 +1,92 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{girror}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Pavel Argentov"]
12
+ s.date = %q{2010-12-31}
13
+ s.default_executable = %q{girror}
14
+ s.description = %q{Retrieves remote directory via SFTP and stores it in local Git repository.}
15
+ s.email = %q{argentoff@gmail.com}
16
+ s.executables = ["girror"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE.txt",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ ".cvsignore",
23
+ ".document",
24
+ "Gemfile",
25
+ "Gemfile.lock",
26
+ "LICENSE.txt",
27
+ "README.rdoc",
28
+ "Rakefile",
29
+ "TODO.md",
30
+ "VERSION",
31
+ "bin/girror",
32
+ "examples/_girror/config.rb",
33
+ "gem_graph.png",
34
+ "girror.gemspec",
35
+ "lib/girror.rb"
36
+ ]
37
+ s.homepage = %q{http://github.com/argent-smith/girror}
38
+ s.licenses = ["MIT"]
39
+ s.require_paths = ["lib"]
40
+ s.rubygems_version = %q{1.3.7}
41
+ s.summary = %q{Remote -> local directory 'mirror' using SFTP transport and Git storage.}
42
+ s.test_files = [
43
+ "examples/_girror/config.rb"
44
+ ]
45
+
46
+ if s.respond_to? :specification_version then
47
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
+ s.add_runtime_dependency(%q<net-sftp>, [">= 0"])
52
+ s.add_runtime_dependency(%q<git>, [">= 0"])
53
+ s.add_runtime_dependency(%q<highline>, [">= 0"])
54
+ s.add_runtime_dependency(%q<syslog-logger>, [">= 0"])
55
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
56
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
57
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
58
+ s.add_development_dependency(%q<rcov>, [">= 0"])
59
+ s.add_runtime_dependency(%q<net-sftp>, [">= 0"])
60
+ s.add_runtime_dependency(%q<git>, [">= 0"])
61
+ s.add_runtime_dependency(%q<highline>, [">= 0"])
62
+ s.add_runtime_dependency(%q<syslog-logger>, [">= 0"])
63
+ else
64
+ s.add_dependency(%q<net-sftp>, [">= 0"])
65
+ s.add_dependency(%q<git>, [">= 0"])
66
+ s.add_dependency(%q<highline>, [">= 0"])
67
+ s.add_dependency(%q<syslog-logger>, [">= 0"])
68
+ s.add_dependency(%q<shoulda>, [">= 0"])
69
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
70
+ s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
71
+ s.add_dependency(%q<rcov>, [">= 0"])
72
+ s.add_dependency(%q<net-sftp>, [">= 0"])
73
+ s.add_dependency(%q<git>, [">= 0"])
74
+ s.add_dependency(%q<highline>, [">= 0"])
75
+ s.add_dependency(%q<syslog-logger>, [">= 0"])
76
+ end
77
+ else
78
+ s.add_dependency(%q<net-sftp>, [">= 0"])
79
+ s.add_dependency(%q<git>, [">= 0"])
80
+ s.add_dependency(%q<highline>, [">= 0"])
81
+ s.add_dependency(%q<syslog-logger>, [">= 0"])
82
+ s.add_dependency(%q<shoulda>, [">= 0"])
83
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
84
+ s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
85
+ s.add_dependency(%q<rcov>, [">= 0"])
86
+ s.add_dependency(%q<net-sftp>, [">= 0"])
87
+ s.add_dependency(%q<git>, [">= 0"])
88
+ s.add_dependency(%q<highline>, [">= 0"])
89
+ s.add_dependency(%q<syslog-logger>, [">= 0"])
90
+ end
91
+ end
92
+
data/lib/girror.rb ADDED
@@ -0,0 +1,275 @@
1
+ # Author:: Pavel Argentov <argentoff@gmail.com>
2
+ # Copyright:: (c) 2010-2011 Pavel Argentov
3
+ # License:: see LICENSE.txt
4
+ #
5
+ # Girror library code, the internals. Since the whole app is a CLI utility,
6
+ # there's not a lot to document here.
7
+ #
8
+ # The library now contains only one module Girror which contains class Application
9
+ # which is the app logic container. The methods have some documentation in the corresponding
10
+ # section (Girror::Application).
11
+ #
12
+ # == Disclaimer
13
+ #
14
+ # This documentation is written as an aid to further development of the program.
15
+ # For more user-friendly documentation see README.rdoc file.
16
+ #
17
+
18
+ ######## Utility
19
+
20
+ $:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
21
+
22
+ # Require all of the Ruby files in the given directory.
23
+ #
24
+ # path - The String relative path from here to the directory.
25
+ #
26
+ # Returns nothing.
27
+ def require_all(path) # :nodoc:
28
+ glob = File.join(File.dirname(__FILE__), path, '*.rb')
29
+ Dir[glob].each do |f|
30
+ require f
31
+ end
32
+ end
33
+
34
+ ######## Requires
35
+ require 'singleton'
36
+ require 'net/sftp'
37
+ require 'fileutils'
38
+ require 'git'
39
+ require 'iconv'
40
+
41
+ # This module encapsulates *girror*'s namespace.
42
+ module Girror
43
+ # Version of the library.
44
+ VERSION = "0.0.1"
45
+
46
+ # Regexp to filter out 'technical' files which aren't normally a part of
47
+ # the mirrored tree and therefore should be _ignored_.
48
+ FILTER_RE = /^((\.((\.{0,1})|((git)(ignore)?)))|(_girror))$/
49
+
50
+ # Application logic container.
51
+ class Application
52
+ include Singleton
53
+
54
+ class << self # class things
55
+ include FileUtils
56
+
57
+ # Runs the app. Much like the C's main().
58
+ def run ops
59
+ # Logging setup
60
+ @log = case ops[:log]
61
+ when 'syslog'
62
+ unless ENV['OS'] == 'Windows_NT'
63
+ require 'syslog_logger'
64
+ SyslogLogger.new('girror')
65
+ else
66
+ Logger.new STDERR
67
+ end
68
+ when nil
69
+ Logger.new STDERR
70
+ else
71
+ Logger.new ops[:log]
72
+ end
73
+ @log.datetime_format = "%Y-%m-%d %H:%M:%S " if Logger.class == Logger
74
+ log "Starting"
75
+ @debug = true if ops[:verbose]
76
+ debug "Current options are: #{ops.inspect}"
77
+
78
+ # check the validity of a local directory
79
+ @lpath = ops[:to] # local save path
80
+ log "Opening local git repo at #{@lpath}"
81
+ @git = Git.open(@lpath) # local git repo
82
+
83
+ cd ops[:to]; log "Changed to #{pwd}"
84
+
85
+ # read the config and use CLI ops to override it
86
+ $:.unshift(File.join(".", "_girror"))
87
+ begin
88
+ require 'config'
89
+ ops = Config::OPTIONS.merge ops
90
+
91
+ begin
92
+ debug "Program options:"
93
+ ops.each do |pair|
94
+ debug pair.inspect
95
+ end
96
+ end
97
+
98
+ rescue LoadError => d
99
+ log "Not using stored config: #{d.message}"
100
+ end
101
+
102
+ # set commit message for git
103
+ ops[:commit_msg].nil? ? @commit_msg = Proc.new { Time.now.to_s } : @commit_msg = ops[:commit_msg]
104
+
105
+ # name conversion encodings for Iconv
106
+ ops[:renc].nil? ? @renc = "utf-8" : @renc = ops[:renc]
107
+ ops[:lenc].nil? ? @lenc = "utf-8" : @lenc = ops[:lenc]
108
+
109
+ # Check the validity of a remote url and run the remote connection
110
+ if ops[:from] =~ /^((\w+)(:(\w+))?@)?(.+):(.*)$/
111
+ $2.nil? ? @user = ENV["USERNAME"] : @user = $2
112
+ @pass = $4
113
+ @host = $5
114
+ @path = $6
115
+
116
+ debug "Remote data specified as: login: #{@user}; pass: #{@pass.inspect}; host: #{@host}; path: #{@path}"
117
+ Net::SFTP.start(@host, @user, :password => @pass) do |s|
118
+ @sftp = s
119
+ log "Connected to remote #{@host} as #{@user}"
120
+
121
+ dl_if_needed @path
122
+
123
+ log "Disconnected from remote #{@host}"
124
+
125
+ # fix the local tree in the git repo
126
+ begin
127
+ log "Committing changes to local git repo"
128
+ @git.add
129
+ msg = if @commit_msg.class == Proc then @commit_msg.call
130
+ else @commit_msg
131
+ end . to_s
132
+ @git.commit msg, :add_all => true
133
+ rescue Git::GitExecuteError => detail
134
+ case detail.message
135
+ when /nothing to commit/
136
+ log "Nothing to commit"
137
+ else
138
+ log detail.message
139
+ end
140
+ end
141
+
142
+ end
143
+
144
+ else
145
+ raise "Bad remote specification!"
146
+ end
147
+
148
+ log "Finishing"
149
+ end
150
+
151
+ # Writes a debug message to @log.
152
+ def debug string
153
+ @log.debug string if @debug
154
+ end
155
+
156
+ # Writes a log message to @log.
157
+ def log string
158
+ @log.info string
159
+ end
160
+
161
+ # On-demand fetcher. Recursively fetches a directory entry 'name' (String)
162
+ # if there's no local copy of it or the remote mtime is newer or the
163
+ # attributes are to be updated.
164
+ def dl_if_needed name
165
+ debug "RNA: #{name}"
166
+ lname = econv(File.join '.', name.gsub(/^#{@path}/,'')); debug "LNA: #{lname}"
167
+
168
+ # get and hold the current direntry's stat in here
169
+ begin
170
+ rs = @sftp.stat!(name); s_rs = [Time.at(rs.mtime), Time.at(rs.atime), rs.uid, rs.gid, "%o" % rs.permissions].inspect
171
+ rescue Net::SFTP::StatusException => detail
172
+ return if detail.code == 2 # silently ignore the broken remote link
173
+ end
174
+ debug "Remote stat for #{name} => #{s_rs}"
175
+
176
+ # remote type filter: we only work with types 1..2 (regular, dir)
177
+ begin
178
+ debug "Remote file type #{rs.type} isn't supported, ignoring."
179
+ return
180
+ end if rs.type > 2
181
+
182
+ # remove the local entry if local/remote entry type differ;
183
+ # else compare remote/local owner/mode and schedule the update.
184
+ if File.exist? lname
185
+ if (
186
+ rs.type != case File.ftype lname
187
+ when "file" then 1
188
+ when "directory" then 2
189
+ end
190
+ )
191
+ remove_entry_secure lname, :force => true
192
+ else
193
+ lrs = File.stat(lname)
194
+ # we do mode comparison on Unices only,
195
+ # and owner compaison only if we are root
196
+ unless ENV['OS'] == "Windows_NT"
197
+ set_attrs = true unless (
198
+ if ENV['EUID'] == 0
199
+ debug "Comparing: #{[rs.permissions, rs.uid, rs.gid].inspect} <=> #{[lrs.mode, lrs.uid, lrs.gid].inspect}"
200
+ [lrs.mode, lrs.uid, lrs.gid] == [rs.permissions, rs.uid, rs.gid]
201
+ else
202
+ debug "Comparing: #{rs.permissions} <=> #{lrs.mode}"
203
+ lrs.mode == rs.permissions
204
+ end
205
+ )
206
+ end
207
+ end
208
+ end
209
+
210
+ # do the type-specific fetch operations
211
+ case rs.type
212
+ when 1
213
+ if (lrs.nil? or (lrs.mtime.to_i < rs.mtime))
214
+ log "Fetching file #{name} -> #{lname.force_encoding("BINARY")} (#{rs.size} bytes)"
215
+ @sftp.download! name, lname
216
+ set_attrs = true
217
+ end
218
+ when 2
219
+ # here we've got a dir
220
+ # create the dir locally if needed
221
+ unless File.exist?(lname)
222
+ log "Fetching directory #{name} -> #{lname.force_encoding("BINARY")} | #{s_rs}"
223
+ mkdir lname
224
+ set_attrs = true
225
+ end
226
+ # recurse into the dir; get the remote list
227
+ rlist = @sftp.dir.entries(name).map do |e|
228
+ unless e.name =~ FILTER_RE
229
+ dl_if_needed(File.join(name, e.name))
230
+ Iconv.conv("utf-8", @renc, e.name)
231
+ end
232
+ end . compact
233
+
234
+ # get the local list
235
+ llist = Dir.entries(lname).map do |n|
236
+ Iconv.conv("utf-8", @lenc, n) unless n =~ FILTER_RE
237
+ end . compact
238
+
239
+ # differentiate the lists; remove what's needed from local repo
240
+ diff = llist - rlist
241
+ diff.each do |n|
242
+ n = File.join(lname, n)
243
+ log "Removing #{n}"
244
+ @git.remove n, :recursive => true
245
+ end
246
+
247
+ end
248
+
249
+ # do the common after-fetch tasks (chown, chmod, utime)
250
+ unless lname == "./"
251
+ unless ENV['OS'] == "Windows_NT" # chmod/chown issues on that platform
252
+ if ENV['EUID'] == 0
253
+ log "Setting owner: #{lname} => #{[rs.uid, rs.gid].inspect}"
254
+ File.chown rs.uid, rs.gid, lname
255
+ end
256
+ log "Setting mode: #{lname} => #{"%o" % rs.permissions}"
257
+ File.chmod rs.permissions, lname
258
+ end
259
+ log "Setting mtime: #{lname} => #{[rs.atime, rs.mtime].map{|t| Time.at(t).strftime("%Y-%m-%d %H:%M:%S")}.inspect}"
260
+ File.utime rs.atime, rs.mtime, lname
261
+ end if set_attrs
262
+ end
263
+
264
+ # Converts the String str from @renc to @lenc if both @renc and @lenc are
265
+ # set and aren't equal.
266
+ #
267
+ # Returns the converted String.
268
+ def econv str
269
+ ((@lenc == @renc) or (@lenc.nil? or @renc.nil?)) ?
270
+ str : Iconv.conv(@lenc, @renc, str)
271
+ end
272
+
273
+ end
274
+ end
275
+ end
metadata ADDED
@@ -0,0 +1,238 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: girror
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Pavel Argentov
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-12-31 00:00:00 +03:00
18
+ default_executable: girror
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: net-sftp
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ prerelease: false
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: git
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
42
+ version: "0"
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: *id002
46
+ - !ruby/object:Gem::Dependency
47
+ name: highline
48
+ requirement: &id003 !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: *id003
59
+ - !ruby/object:Gem::Dependency
60
+ name: syslog-logger
61
+ requirement: &id004 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ segments:
67
+ - 0
68
+ version: "0"
69
+ type: :runtime
70
+ prerelease: false
71
+ version_requirements: *id004
72
+ - !ruby/object:Gem::Dependency
73
+ name: shoulda
74
+ requirement: &id005 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: *id005
85
+ - !ruby/object:Gem::Dependency
86
+ name: bundler
87
+ requirement: &id006 !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ~>
91
+ - !ruby/object:Gem::Version
92
+ segments:
93
+ - 1
94
+ - 0
95
+ - 0
96
+ version: 1.0.0
97
+ type: :development
98
+ prerelease: false
99
+ version_requirements: *id006
100
+ - !ruby/object:Gem::Dependency
101
+ name: jeweler
102
+ requirement: &id007 !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ~>
106
+ - !ruby/object:Gem::Version
107
+ segments:
108
+ - 1
109
+ - 5
110
+ - 1
111
+ version: 1.5.1
112
+ type: :development
113
+ prerelease: false
114
+ version_requirements: *id007
115
+ - !ruby/object:Gem::Dependency
116
+ name: rcov
117
+ requirement: &id008 !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ segments:
123
+ - 0
124
+ version: "0"
125
+ type: :development
126
+ prerelease: false
127
+ version_requirements: *id008
128
+ - !ruby/object:Gem::Dependency
129
+ name: net-sftp
130
+ requirement: &id009 !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ segments:
136
+ - 0
137
+ version: "0"
138
+ type: :runtime
139
+ prerelease: false
140
+ version_requirements: *id009
141
+ - !ruby/object:Gem::Dependency
142
+ name: git
143
+ requirement: &id010 !ruby/object:Gem::Requirement
144
+ none: false
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ segments:
149
+ - 0
150
+ version: "0"
151
+ type: :runtime
152
+ prerelease: false
153
+ version_requirements: *id010
154
+ - !ruby/object:Gem::Dependency
155
+ name: highline
156
+ requirement: &id011 !ruby/object:Gem::Requirement
157
+ none: false
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ segments:
162
+ - 0
163
+ version: "0"
164
+ type: :runtime
165
+ prerelease: false
166
+ version_requirements: *id011
167
+ - !ruby/object:Gem::Dependency
168
+ name: syslog-logger
169
+ requirement: &id012 !ruby/object:Gem::Requirement
170
+ none: false
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ segments:
175
+ - 0
176
+ version: "0"
177
+ type: :runtime
178
+ prerelease: false
179
+ version_requirements: *id012
180
+ description: Retrieves remote directory via SFTP and stores it in local Git repository.
181
+ email: argentoff@gmail.com
182
+ executables:
183
+ - girror
184
+ extensions: []
185
+
186
+ extra_rdoc_files:
187
+ - LICENSE.txt
188
+ - README.rdoc
189
+ files:
190
+ - .cvsignore
191
+ - .document
192
+ - Gemfile
193
+ - Gemfile.lock
194
+ - LICENSE.txt
195
+ - README.rdoc
196
+ - Rakefile
197
+ - TODO.md
198
+ - VERSION
199
+ - bin/girror
200
+ - examples/_girror/config.rb
201
+ - gem_graph.png
202
+ - girror.gemspec
203
+ - lib/girror.rb
204
+ has_rdoc: true
205
+ homepage: http://github.com/argent-smith/girror
206
+ licenses:
207
+ - MIT
208
+ post_install_message:
209
+ rdoc_options: []
210
+
211
+ require_paths:
212
+ - lib
213
+ required_ruby_version: !ruby/object:Gem::Requirement
214
+ none: false
215
+ requirements:
216
+ - - ">="
217
+ - !ruby/object:Gem::Version
218
+ hash: 452461409
219
+ segments:
220
+ - 0
221
+ version: "0"
222
+ required_rubygems_version: !ruby/object:Gem::Requirement
223
+ none: false
224
+ requirements:
225
+ - - ">="
226
+ - !ruby/object:Gem::Version
227
+ segments:
228
+ - 0
229
+ version: "0"
230
+ requirements: []
231
+
232
+ rubyforge_project:
233
+ rubygems_version: 1.3.7
234
+ signing_key:
235
+ specification_version: 3
236
+ summary: Remote -> local directory 'mirror' using SFTP transport and Git storage.
237
+ test_files:
238
+ - examples/_girror/config.rb