#!/usr/bin/env ruby

# == Synopsis
#
# hello: greets user, demonstrates command line parsing
#
#== Usage
#
# scrpt [OPTION] ... DIR
#
# -h, --help:
#    show help
#
# -d, --dry-run
#    do not modify config files
#
# -g dir, --gitorious dir
#    set gitorious base dir
#    default ( /var/www/gitorious )
# -m, --mysql
#    setup mysql, default off
# -p, --postgresql
#    setup postgresql, default off (broken)
# -H, --host <host>
#    database server host
#    default ( '' )
# -p, --port <port>
#    database server port
# -p, --port <port>
#    database server port
# -S, --socket <socket directory>
#    path to directory with unix sockets
# -a, --adminuser <user>
#    username for admin user
# -A, --adminpass <passwd>
#    password for admin user
# -n, --newuser <user>
#    usesrname of the new database user
# -N, --newpass <passwd>
#    password for the new database user
# -D, --database <name>
#    database name
# -E, --railsenv <env>
#    RAILS_ENV=<env>

require 'rubygems'
require 'ftools'
require 'logger'
require 'sha1'
require 'yaml'
require 'socket'
require 'pp'
require 'diff/lcs'
require 'tempfile'

require 'getoptlong'
require 'rdoc/usage'

log = Logger.new(STDOUT)
log.level = Logger::WARN

opts = GetoptLong.new(
  [ '--help',		'-h', GetoptLong::NO_ARGUMENT ],
  [ '--dry-run',	'-d', GetoptLong::NO_ARGUMENT ],
  [ '--verbose',	'-v', GetoptLong::NO_ARGUMENT ],
  [ '--gitorious',	'-g', GetoptLong::REQUIRED_ARGUMENT ],
  [ '--mysql',		'-m', GetoptLong::NO_ARGUMENT ],
  [ '--postgresql',	'-p', GetoptLong::NO_ARGUMENT ],
  [ '--host',		'-H', GetoptLong::REQUIRED_ARGUMENT ],
  [ '--port',		'-P', GetoptLong::REQUIRED_ARGUMENT ],
  [ '--socket',		'-S', GetoptLong::REQUIRED_ARGUMENT ],
  [ '--adminuser',	'-a', GetoptLong::REQUIRED_ARGUMENT ],
  [ '--adminpass',	'-A', GetoptLong::REQUIRED_ARGUMENT ],
  [ '--newuser',	'-n', GetoptLong::REQUIRED_ARGUMENT ],
  [ '--newpass',	'-N', GetoptLong::REQUIRED_ARGUMENT ],
  [ '--database',	'-D', GetoptLong::REQUIRED_ARGUMENT ],
  [ '--railsenv',	'-E', GetoptLong::REQUIRED_ARGUMENT ]
)

dry_run = false
gitorious_dir = "/var/www/gitorious"
adapter = nil
db_host = nil
db_port = nil
db_socket = nil
db_adminuser = nil
db_adminpass = nil
db_newuser = "gitorious"
db_newpass = nil
db_name = "gitorious"
rails_env = "production"

user = Etc.getlogin()
git_uid = Etc.getpwnam("git")['uid']

opts.each do |opt, arg|
  case opt
  when '--help'
    RDoc::usage
  when '--dry-run'
    dry_run = true
  when '--gitorious'
    raise "#{arg} does not exist" if not File.directory?(arg)
    gitorious_dir = arg.to_s
  when '--mysql'
    adapter = "mysql"
  when '--postgresql'
    adapter = "postgresql"
  when '--host'
    db_host = arg.to_s
  when '--port'
    db_port = arg.to_s
  when '--socket'
    db_socket = arg.to_s
  when '--adminuser'
    db_adminuser = arg.to_s
  when '--adminpass'
    db_adminpass = arg.to_s
  when '--newuser'
    db_newuser = arg.to_s
  when '--newpass'
    db_newpass = arg.to_s
  when '--database'
    db_name = arg.to_s
  when '--railsenv'
    rails_env = arg.to_s
  when '--verbose'
    log.level = Logger::INFO
  end
end

# basic gitorious setup
Dir.chdir(gitorious_dir) do
  config = YAML.load_file('config/gitorious.yml')

  target = Tempfile.new('gitorious')

  config[rails_env] = {} if not config.has_key?(rails_env)

  hostname = Socket.gethostbyname(Socket.gethostname).first
  log.info "Setting hostname to #{hostname}"
  for h in ['gitorious_host','gitorious_client_host'] do
    if not config[rails_env].has_key?(h) or \
      config[rails_env][h] == "localhost" or \
      config[rails_env][h] == nil
      config[rails_env][h] = hostname
    end
  end

  if not config[rails_env].has_key?('cookie_secret') or \
    config[rails_env]['cookie_secret'] == "ssssht" or \
    config[rails_env]['cookie_secret'] == nil

    srand
    seed = "--#{rand(10000)}--#{Time.now}--"
    secret = Digest::SHA1.hexdigest(seed)
    config[rails_env]['cookie_secret'] = secret    
    log.info "Secret set to #{secret}"
  end

  target.write(config.to_yaml(:SortKeys	=> true))
  target.close
  log.info "write config/gitorious.yml"
  File.mv(target.path, 'config/gitorious.yml') if not dry_run
  if user == "root"
    File.new('config/gitorious.yml').chown(git_uid, 0)
  end

end

# database setup
Dir.chdir(gitorious_dir) do
  if adapter
    # it works, create a database.yml file and change the
    # mysql password
    config = YAML.load_file('config/database.sample.yml')
    target = Tempfile.new('gitorious-database')

    srand
    seed = "--#{rand(10000)}--#{Time.now}--"
    # create a 5 characters password
    secret = Digest::SHA1.hexdigest(seed)[0,6]
    log.info "create datebase password..."      

    config.delete(rails_env)

    config[rails_env] = {}
    config[rails_env]['adapter'] = adapter
    config[rails_env]['database'] = db_name
    config[rails_env]['username'] = db_newuser
    config[rails_env]['password'] = secret
    config[rails_env]['host'] = db_host
    config[rails_env]['encoding'] = 'utf8'

    if adapter == "mysql"
      require 'mysql'
      log.info "mysql setup..."

      db_adminuser = "root" if not db_adminuser

      my = Mysql::new(host=db_host, user=db_adminuser, passwd=db_adminpass, port=db_port, socket=db_socket)
      if not my.query("SHOW databases LIKE '#{db_name}'").fetch_row()
	log.info "Creating database"
	my.query("CREATE DATABASE #{db_name}")
      end

      log.info "Setting mysql password..."
      if not my.query("SELECT user FROM mysql.user WHERE user='#{db_newuser}'").fetch_row()
	my.query("create user #{db_newuser}@localhost identified by '#{secret}'")
      end
      my.query("grant all privileges on #{db_name}.* to #{db_newuser}")
      my.close()
    elsif adapter == "postgresql"
      require 'postgres'

      db_adminuser = "postgres" if not db_adminuser

      pg = PGconn.open("host='#{db_host}' port='#{db_port}' user='#{db_adminuser}' password='#{db_adminpass}'")
      if pg.query("select usename from pg_catalog.pg_user where usename = '#{db_newuser}'").length == 0
	log.info "Creating user: '#{db_name}'"
	pg.exec("CREATE ROLE #{db_newuser} PASSWORD '#{secret}' VALID UNTIL 'infinity' NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT LOGIN")
      end
      if pg.query("select datname from pg_catalog.pg_database where datname = '#{db_name}'").length == 0
	log.info "Creating database: '#{db_name}'"
	pg.exec("CREATE DATABASE #{db_name} WITH OWNER = #{db_newuser} TABLESPACE = pg_default")
      end
      pg.exec("UPDATE pg_database SET ENCODING = pg_catalog.pg_char_to_encoding('UTF8') WHERE datname='#{db_name}'")
      pg.exec("\c #{db_name}; GRANT ALL ON SCHEMA pg.public TO #{db_newuser}")      
      pg.close()
      pg = PGconn.open("host='#{db_host}' port='#{db_port}' dbname='#{db_name}' user='#{db_newuser}' password='#{db_newpass}'")

      if pg.query("select lanname from pg_catalog.pg_language where lanname = 'plpgsql'").length == 0
	pg.exec("CREATE LANGUAGE 'plpgsql'")
      end

      functions = Dir.glob('vendor/plugins/ultrasphinx/lib/ultrasphinx/postgresql/*.sql')
      for f in functions do
	if File.basename(f) != "language.sql"
  	  sql = File.open(f, "r")
  	  pg.exec(sql.read())
  	  sql.close()
	end
      end

      pg.close()

      log.info "Escaping 'user' non-keyword in config/ultrasphinx/*.conf for postgresql"
      system("perl -p -i -e 's/ user([^s]{1})/ \"user\"$1/g' config/ultrasphinx/*.conf")
    end

    target.write(config.to_yaml(:SortKeys	=> true))
    target.close

    log.info "write config/database.yml"
    File.mv(target.path, 'config/database.yml') if not dry_run
    if user == "root"
      File.new('config/database.yml').chown(git_uid, 0)
    end

    log.info "Configuring ultrasphinx"
    system("rake ultrasphinx:configure RAILS_ENV=#{rails_env} ") if not dry_run

    log.info "Migrating database"
    system("rake db:migrate RAILS_ENV=#{rails_env}") if not dry_run

    log.info "Index ultrasphinx"
    system("rake ultrasphinx:index RAILS_ENV=#{rails_env}") if not dry_run

  end
end
