class Girror::Application
Application
logic container.
Public Class Methods
debug(string)
click to toggle source
Writes a debug message to @log.
# File lib/girror.rb, line 156 def debug string @log.debug string if @debug end
dl_if_needed(name)
click to toggle source
On-demand fetcher. Recursively fetches a directory entry ‘name’ (String) if there’s no local copy of it or the remote mtime is newer or the attributes are to be updated.
# File lib/girror.rb, line 168 def dl_if_needed name debug "RNA: #{name}" lname = econv(File.join '.', name.gsub(/^#{@path}/,'')); debug "LNA: #{lname}" # get and hold the current direntry's stat in here begin rs = @sftp.stat!(name); s_rs = [Time.at(rs.mtime), Time.at(rs.atime), rs.uid, rs.gid, "%o" % rs.permissions].inspect rescue Net::SFTP::StatusException => detail return if detail.code == 2 # silently ignore the broken remote link end debug "Remote stat for #{name} => #{s_rs}" # remote type filter: we only work with types 1..2 (regular, dir) begin debug "Remote file type #{rs.type} isn't supported, ignoring." return end if rs.type > 2 # remove the local entry if local/remote entry type differ; # else compare remote/local owner/mode and schedule the update. if File.exist? lname if ( rs.type != case File.ftype lname when "file" then 1 when "directory" then 2 end ) remove_entry_secure lname, :force => true else lrs = File.stat(lname) # we do mode comparison on Unices only, # and owner compaison only if we are root unless ENV['OS'] == "Windows_NT" set_attrs = true unless ( if ENV['EUID'] == 0 debug "Comparing: #{[rs.permissions, rs.uid, rs.gid].inspect} <=> #{[lrs.mode, lrs.uid, lrs.gid].inspect}" [lrs.mode, lrs.uid, lrs.gid] == [rs.permissions, rs.uid, rs.gid] else debug "Comparing: #{rs.permissions} <=> #{lrs.mode}" lrs.mode == rs.permissions end ) end end end # do the type-specific fetch operations case rs.type when 1 if (lrs.nil? or (lrs.mtime.to_i < rs.mtime)) log "Fetching file #{name} -> #{lname.force_encoding("BINARY")} (#{rs.size} bytes)" @sftp.download! name, lname set_attrs = true end when 2 # here we've got a dir # create the dir locally if needed unless File.exist?(lname) log "Fetching directory #{name} -> #{lname.force_encoding("BINARY")} | #{s_rs}" mkdir lname set_attrs = true end # recurse into the dir; get the remote list rlist = @sftp.dir.entries(name).map do |e| unless e.name =~ FILTER_RE dl_if_needed(File.join(name, e.name)) Iconv.conv("utf-8", @renc, e.name) end end . compact # get the local list llist = Dir.entries(lname).map do |n| Iconv.conv("utf-8", @lenc, n) unless n =~ FILTER_RE end . compact # differentiate the lists; remove what's needed from local repo diff = llist - rlist diff.each do |n| # the string should be converted back to local encoding before any # operations n = Iconv.conv(@lenc, "utf-8", File.join(lname, n)) log "Removing #{n}" begin @git.remove n, :recursive => true rescue Git::GitExecuteError => detail case detail.message when /did not match/ log "#{n} has no match in the git repo: removing it forcefully!" rm_rf n else log detail.message end end end end # do the common after-fetch tasks (chown, chmod, utime) unless lname == "./" unless ENV['OS'] == "Windows_NT" # chmod/chown issues on that platform if ENV['EUID'] == 0 log "Setting owner: #{lname} => #{[rs.uid, rs.gid].inspect}" File.chown rs.uid, rs.gid, lname end log "Setting mode: #{lname} => #{"%o" % rs.permissions}" File.chmod rs.permissions, lname end log "Setting mtime: #{lname} => #{[rs.atime, rs.mtime].map{|t| Time.at(t).strftime("%Y-%m-%d %H:%M:%S")}.inspect}" File.utime rs.atime, rs.mtime, lname end if set_attrs end
econv(str)
click to toggle source
Converts the String str from @renc to @lenc if both @renc and @lenc are set and aren’t equal.
Returns the converted String.
# File lib/girror.rb, line 283 def econv str ((@lenc == @renc) or (@lenc.nil? or @renc.nil?)) ? str : Iconv.conv(@lenc, @renc, str) end
log(string)
click to toggle source
Writes a log message to @log.
# File lib/girror.rb, line 161 def log string @log.info string end
run(ops)
click to toggle source
Runs the app. Much like the C’s main().
# File lib/girror.rb, line 58 def run ops # Logging setup @log = case ops[:log] when 'syslog' unless ENV['OS'] == 'Windows_NT' require 'syslog_logger' SyslogLogger.new('girror') else Logger.new STDERR end when nil Logger.new STDERR else Logger.new ops[:log] end @log.datetime_format = "%Y-%m-%d %H:%M:%S " if Logger.class == Logger log "Starting" @debug = true if ops[:verbose] debug "Current options are: #{ops.inspect}" # check the validity of a local directory @lpath = ops[:to] # local save path log "Opening local git repo at #{@lpath}" @git = Git.open(@lpath) # local git repo cd ops[:to]; log "Changed to #{pwd}" # read the config and use CLI ops to override it $:.unshift(File.join(".", "_girror")) begin require 'config' ops = Config::OPTIONS.merge ops begin debug "Program options:" ops.each do |pair| debug pair.inspect end end rescue LoadError => d log "Not using stored config: #{d.message}" end # set commit message for git ops[:commit_msg].nil? ? @commit_msg = Proc.new { Time.now.to_s } : @commit_msg = ops[:commit_msg] # name conversion encodings for Iconv ops[:renc].nil? ? @renc = "utf-8" : @renc = ops[:renc] ops[:lenc].nil? ? @lenc = "utf-8" : @lenc = ops[:lenc] # Check the validity of a remote url and run the remote connection if ops[:from] =~ /^((\w+)(:(\w+))?@)?(.+):(.*)$/ $2.nil? ? @user = ENV["USERNAME"] : @user = $2 @pass = $4 @host = $5 @path = $6 debug "Remote data specified as: login: #{@user}; pass: #{@pass.inspect}; host: #{@host}; path: #{@path}" Net::SFTP.start(@host, @user, :password => @pass, :keys => ops[:ssh][:keys], :compression => ops[:ssh][:compression] ) do |s| @sftp = s log "Connected to remote #{@host} as #{@user}" dl_if_needed @path log "Disconnected from remote #{@host}" # fix the local tree in the git repo begin log "Committing changes to local git repo" @git.add msg = if @commit_msg.class == Proc then @commit_msg.call else @commit_msg end . to_s @git.commit msg, :add_all => true rescue Git::GitExecuteError => detail case detail.message when /nothing to commit/ log "Nothing to commit" else log detail.message end end end else raise "Bad remote specification!" end log "Finishing" end