apache/conf/original/extra/httpd-vhosts.conf
apache/conf/conf.d/*.conf
apache/logs
+backup
config/service.yml
hudson/hudson.xml
hudson/home/*.log
--- /dev/null
+@echo off\r
+setlocal\r
+call "%~dp0setenv.bat"\r
+ruby "%~dp0backup" %*
\ No newline at end of file
--- /dev/null
+require 'find'
+require 'service'
+
+module BackupRestore
+ INSTALL_DIR = File.expand_path(ENV["PACKAGE_HOME"])
+
+ def targets
+ unless @targets
+ @targets = %w[hudson opends redmine subversion]
+ @targets.delete("opends") unless Service.config["opends"]
+ end
+ @targets
+ end
+
+ def sync(src, dest, excludes=nil)
+ [src, dest].each {|d|
+ raise "#{d} is not directory." unless File.directory?(d)
+ }
+
+ src = File.expand_path(src)
+ dest = File.expand_path(dest)
+ updated = {}
+
+ Dir.chdir(dest) {
+ Find.find(".") {|old_file|
+ next if old_file == "."
+ Find.prune if excludes && excludes.any? {|ex| old_file.include?(ex)}
+
+ new_file = File.join(src, old_file)
+ unless File.exists?(new_file)
+ puts "Remove " + File.expand_path(old_file)
+ FileUtils.rm_rf(old_file)
+ next
+ end
+
+ next if File.directory?(old_file)
+
+ old_st = File.stat(old_file)
+ new_st = File.stat(new_file)
+ if old_st.mtime != new_st.mtime || old_st.size != new_st.size
+ updated[old_file] = true
+ File.delete(old_file)
+ end
+ }
+ }
+
+ Dir.chdir(src) {
+ Find.find(".") {|from|
+ next if from == "."
+ Find.prune if excludes && excludes.any? {|ex| from.include?(ex)}
+
+ to = File.join(dest, from[1..-1])
+ next if File.exists?(to)
+
+ puts (updated[from] ? "Update " : "Add ") + to
+ if File.directory?(from)
+ Dir.mkdir(to)
+ else
+ st = File.stat(from)
+ FileUtils.cp(from, to)
+ File.utime(st.atime, st.mtime, to)
+ end
+ }
+ }
+ end
+
+ def system_or_raise(command)
+ raise "\"#{command}\" failed" unless system command
+ end
+
+ def exec(method, args)
+ method = method.to_sym
+ target, dir = args
+ unless target == "all" || targets.include?(target)
+ raise "invalid target #{target}"
+ end
+ dir = dir ? File.expand_path(dir) : File.join(INSTALL_DIR, "backup")
+
+ service_script = File.join(INSTALL_DIR, "script/service.bat")
+ system(service_script, "stop") if method == :restore
+
+ Dir.chdir(INSTALL_DIR) {
+ (target == "all" ? targets : [target]).each {|target|
+ puts "--- #{target} ---"
+
+ backup_dir = File.join(dir, target)
+ case method
+ when :backup
+ FileUtils.mkdir_p(backup_dir)
+ when :restore
+ unless File.directory?(backup_dir)
+ puts "Backup files not found"
+ next
+ end
+ else
+ raise "Invalid method #{method}"
+ end
+
+
+ case target
+ when "hudson"
+ hudson_dir = "hudson/home"
+ excludes = ["/workspace", "./plugins", "./updates", "./war"]
+ if method == :backup
+ sync(hudson_dir, backup_dir, excludes)
+ else
+ sync(backup_dir, hudson_dir, excludes)
+ end
+
+ when "opends"
+ Dir.chdir("opends/bat") {
+ if method == :backup
+ FileUtils.rm_rf(backup_dir)
+ system_or_raise(%[backup --compress -a -d "#{backup_dir}"])
+ else
+ Dir.glob(backup_dir + "/*/").each {|d|
+ system_or_raise(%[restore -d "#{d}"])
+ }
+ end
+ }
+
+ when "redmine"
+ backup_db = backup_dir + "/redmine.db"
+ files_dir = backup_dir + "/files"
+
+ if method == :backup
+ puts "Backup database"
+ system_or_raise(%[sqlite3 redmine/db/redmine.db ".backup '#{backup_db}'"])
+ Dir.mkdir(files_dir) unless File.exists?(files_dir)
+ sync("redmine/files", files_dir)
+ else
+ if File.file?(backup_db)
+ puts "Restore database"
+ system_or_raise(%[sqlite3 "#{backup_db}" ".restore redmine/db/redmine.db"])
+ end
+
+ sync(files_dir, "redmine/files") if File.directory?(files_dir)
+ end
+
+ when "subversion"
+ if method == :backup
+ Dir.chdir("subversion/repos") {
+ Dir.glob("*/").each {|svndir|
+ puts "Backup repository #{svndir[0..-2]}"
+ to = File.join(backup_dir, svndir)
+ FileUtils.rm_rf(to)
+ system_or_raise(%[svnadmin hotcopy #{svndir} "#{to}"])
+ }
+ }
+ else
+ svndirs = Dir.chdir(backup_dir) {Dir.glob("*/")}
+ svndirs.each {|svndir|
+ puts "Restore repository #{svndir[0..-2]}"
+ from = File.join(backup_dir, svndir)
+ to = "subversion/repos/" + svndir
+ FileUtils.rm_rf(to)
+ system_or_raise(%[svnadmin hotcopy #{from} #{to}])
+ }
+ end
+ end
+ }
+ }
+
+ system(service_script, "start") if method == :restore
+ end
+
+ def backup(args)
+ exec :backup, args
+ end
+
+ def restore(args)
+ exec :restore, args
+ end
+
+ def restore_usage
+ warn <<-EOT
+Usage: restore <target> [<source>]
+
+By default, restore from RedmineLE/backup directory.
+
+Targets:
+ all
+ #{targets.join("\n ")}
+ EOT
+ end
+end
--- /dev/null
+require 'yaml'
+require 'win32/service'
+
+class Service
+ def self.config
+ @config ||=
+ YAML.load_file(File.join(ENV["PACKAGE_HOME"], "config/service.yml"))
+ end
+
+ def self.names
+ @names ||= config.values.map {|h| h["service_name"]}
+ end
+
+ def self.exists?(name)
+ !!(Win32::Service.status(name) rescue nil)
+ end
+
+ def self.status(name)
+ s = Win32::Service.status(name) rescue nil
+ s && s.current_state
+ end
+end
+
--- /dev/null
+#!/usr/bin/env ruby
+
+require 'backup_restore'
+require 'windows/authorization'
+
+class Restore
+ extend BackupRestore
+
+ def self.usage
+ warn <<-EOT
+Usage: restore <target> [<source>]
+
+By default, restore from RedmineLE/backup directory.
+
+Targets:
+ all
+ #{targets.join("\n ")}
+ EOT
+ end
+end
+
+if ARGV.length < 1
+ Restore.usage
+ exit
+end
+
+Windows::Authorization.runas_admin
+
+print <<EOT
+=== Warning ===
+Current data will be overwritten by backup data.
+The service will be stopped and restarted automatically.
+
+=== Confirmation ===
+EOT
+print "Are you sure you want to continue? (y/[n]): "
+
+exit if STDIN.gets[0,1].downcase != "y"
+
+Restore.restore ARGV
--- /dev/null
+@echo off\r
+setlocal\r
+call "%~dp0setenv.bat"\r
+ruby "%~dp0restore" %*
\ No newline at end of file
goto :end\r
\r
#!ruby\r
-require 'yaml'\r
+require 'service'\r
require 'win32/service'\r
require 'windows/authorization'\r
require 'highline/import'\r
\r
module Command\r
def install\r
- service_names.each {|name|\r
- if (Win32::Service.status(name) rescue nil)\r
+ Service.names.each {|name|\r
+ if Service.exists?(name)\r
warn "Error: service #{name} already exists"\r
exit 1\r
end\r
\r
wrapper = File.join(PACKAGE_HOME, "script/wrapper.bat")\r
\r
- config.each {|key, conf|\r
+ Service.config.each {|key, conf|\r
name = conf["service_name"]\r
port = conf["port"]\r
\r
\r
def uninstall\r
stop(false)\r
- service_names.each {|name|\r
+ Service.names.each {|name|\r
next unless status(name)\r
\r
begin\r
end\r
\r
def start(verbose=true)\r
- service_names.each {|name|\r
- next unless s = status(name, verbose)\r
+ Service.names.each {|name|\r
+ s = Service.status(name)\r
+ unless s\r
+ warn "service #{name} is not installed" if verbose\r
+ next\r
+ end\r
+\r
if s == "running"\r
warn "service #{name} is already running" if verbose\r
next\r
\r
begin\r
Win32::Service.start(name)\r
- sleep 0.1 while status(name, false) == "start pending"\r
- raise if status(name, false) != "running"\r
+ sleep 0.1 while Service.status(name) == "start pending"\r
+ raise if Service.status(name) != "running"\r
warn "service #{name} started"\r
rescue\r
warn "Error: failed to start service #{name}"\r
end\r
\r
def stop(verbose=true)\r
- service_names.each {|name|\r
- next unless s = status(name, verbose)\r
+ Service.names.each {|name|\r
+ s = Service.status(name)\r
+ unless s\r
+ warn "service #{name} is not installed" if verbose\r
+ next\r
+ end\r
+\r
if s == "stopped"\r
warn "service #{name} has already been stopped" if verbose\r
next\r
\r
begin\r
Win32::Service.stop(name)\r
- sleep 0.1 while status(name, false) != "stopped"\r
+ sleep 0.1 while Service.status(name) != "stopped"\r
warn "service #{name} stopped"\r
rescue\r
warn "Error: failed to stop service #{name}"\r
start\r
end\r
\r
- def status(name=nil, verbose=true)\r
- if name\r
- service = Win32::Service.status(name) rescue nil\r
- warn "service #{name} is not installed" if verbose && service.nil?\r
- return service && service.current_state\r
- end\r
-\r
- service_names.each {|name|\r
- begin\r
- status = Win32::Service.status(name).current_state\r
- warn "#{name}: #{status}"\r
- rescue\r
- warn "#{name}: not installed"\r
- end\r
- }\r
- nil\r
- end\r
end\r
\r
class Service\r
extend Command\r
\r
class << self\r
- def config\r
- @config ||= YAML.load_file(File.join(PACKAGE_HOME, "config/service.yml"))\r
- end\r
-\r
- def service_names\r
- @service_names ||= config.values.map {|h| h["service_name"]}\r
- end\r
-\r
def usage\r
warn <<EOT\r
Usage: service <command>\r
\r
Commands:\r
- #{Command.instance_methods.sort.join("\n ")}\r
+ #{Command.instance_methods.push("status").sort.join("\n ")}\r
EOT\r
exit 1\r
end\r
\r
+ def display_status\r
+ names.each {|name|\r
+ if s = status(name)\r
+ warn "#{name}: #{s}"\r
+ else\r
+ warn "#{name}: not installed"\r
+ end\r
+ }\r
+ end\r
+\r
def method_missing(name, *args)\r
warn "Unknown command #{name}"\r
usage\r
\r
(command, pause) = ARGV[0..1]\r
Service.usage unless command\r
-if %w[install uninstall start stop restart].include?(command)\r
+if command == "status"\r
+ Service.display_status\r
+else\r
Windows::Authorization.runas_admin\r
+ Service.send(command)\r
end\r
-Service.send(command)\r
\r
ask("Press any key to exit ... ") {|q|\r
q.character = true\r