class RedmineInstaller::Redmine

Constants

BACKUP_EXCLUDE_FILES
CHECK_N_INACCESSIBLE_FILES
DEFAULT_BACKUP_ROOT
FILES_DIR
REQUIRED_FILES

Attributes

database[R]
root[RW]

Public Class Methods

new(task, root=nil) click to toggle source
Calls superclass method
# File lib/redmine-installer/redmine.rb, line 29
def initialize(task, root=nil)
  super(task)
  @root = root.to_s

  if (dump = task.options.database_dump)
    @database_dump_to_load = File.expand_path(dump)
  end
end

Public Instance Methods

bundle_path() click to toggle source
# File lib/redmine-installer/redmine.rb, line 66
def bundle_path
  File.join(root, '.bundle')
end
check_running_state() click to toggle source

Check if redmine is running based on PID files.

# File lib/redmine-installer/redmine.rb, line 166
def check_running_state
  if running?
    if prompt.yes?("Your app is running based on PID files (#{pids_files.join(', ')}). Do you want continue?", default: false)
      logger.warn("App is running (pids: #{pids_files.join(', ')}). Ignore it and continue.")
    else
      error('App is running')
    end
  end
end
clean_up() click to toggle source
# File lib/redmine-installer/redmine.rb, line 548
def clean_up
end
configuration_yml_path() click to toggle source
# File lib/redmine-installer/redmine.rb, line 42
def configuration_yml_path
  File.join(root, 'config', 'configuration.yml')
end
copy_importants_from(other_redmine) click to toggle source

Copy important files which cannot be deleted

# File lib/redmine-installer/redmine.rb, line 346
def copy_importants_from(other_redmine)
  Dir.chdir(root) do
    # Copy database.yml
    FileUtils.cp(other_redmine.database_yml_path, database_yml_path)

    # Copy configuration.yml
    if File.exist?(other_redmine.configuration_yml_path)
      FileUtils.cp(other_redmine.configuration_yml_path, configuration_yml_path)
    end

    # Copy Gemfile.local
    if File.exist?(other_redmine.gemfile_local_path)
      FileUtils.cp(other_redmine.gemfile_local_path, gemfile_local_path)
    end

    # Copy files
    if task.options.copy_files_with_symlink
      FileUtils.rm_rf(files_path)
      FileUtils.ln_s(other_redmine.files_path, root)
    else
      FileUtils.cp_r(other_redmine.files_path, root)
    end

    # Copy old logs
    FileUtils.mkdir_p(log_path)
    Dir.glob(File.join(other_redmine.log_path, 'redmine_installer_*')).each do |log|
      FileUtils.cp(log, log_path)
    end

    # Copy bundle config
    if Dir.exist?(other_redmine.bundle_path)
      FileUtils.mkdir_p(bundle_path)
      FileUtils.cp_r(other_redmine.bundle_path, root)
    end
  end

  # Copy 'keep' files (base on options)
  Array(task.options.keep).each do |path|
    origin_path = File.join(other_redmine.root, path)
    next unless File.exist?(origin_path)

    # Ensure folder
    target_dir = File.join(root, File.dirname(path))
    FileUtils.mkdir_p(target_dir)

    # Copy recursive
    FileUtils.cp_r(origin_path, target_dir)
  end

  logger.info('Important files was copyied')
end
copy_missing_plugins_from(other_redmine) click to toggle source

New package may not have all plugins

# File lib/redmine-installer/redmine.rb, line 424
def copy_missing_plugins_from(other_redmine)
  missing = []

  yield_missing_plugins(other_redmine.plugins_path, plugins_path) do |from, to|
    missing << [from, to]
  end

  yield_missing_plugins(other_redmine.easy_plugins_path, easy_plugins_path) do |from, to|
    missing << [from, to]
  end

  missing_plugin_names = missing.map{|(from, to)| File.basename(from) }
  logger.info("Missing plugins: #{missing_plugin_names.join(', ')}")

  if missing.empty?
    return
  end

  puts
  if !prompt.yes?("Your application contains plugins that are not present in the package (#{missing_plugin_names.join(', ')}). Would you like to copy them?")
    return
  end

  missing.each do |(from, to)|
    FileUtils.cp_r(from, to)
    logger.info("Copied #{from} to #{to}")
  end
end
create_configuration_yml() click to toggle source

Create and configure configuration For now only email

# File lib/redmine-installer/redmine.rb, line 188
def create_configuration_yml
  print_title('Creating email configuration')

  @configuration = Configuration.create_config(self)
  logger.info("Configuration initialized #{@configuration}")
end
create_database_yml() click to toggle source

Create and configure rails database

# File lib/redmine-installer/redmine.rb, line 178
def create_database_yml
  print_title('Creating database configuration')

  @database = Database.create_config(self)
  logger.info("Database initialized #{@database}")
end
database_yml_path() click to toggle source
# File lib/redmine-installer/redmine.rb, line 38
def database_yml_path
  File.join(root, 'config', 'database.yml')
end
delete_root() click to toggle source

# => ['.', '..'] def empty_root?

Dir.entries(root).size <= 2

end

# File lib/redmine-installer/redmine.rb, line 276
def delete_root
  Dir.chdir(root) do
    Dir.entries('.').each do |entry|
      next if entry == '.' || entry == '..'
      next if entry == FILES_DIR && task.options.copy_files_with_symlink

      FileUtils.remove_entry_secure(entry)
    end
  end

  logger.info("#{root} content was deleted")
end
easy_plugins_path() click to toggle source
# File lib/redmine-installer/redmine.rb, line 58
def easy_plugins_path
  File.join(plugins_path, 'easyproject', 'easy_plugins')
end
ensure_and_valid_root() click to toggle source

Ask for REDMINE_ROOT (if wasnt set) and check access rights

# File lib/redmine-installer/redmine.rb, line 105
def ensure_and_valid_root
  if root.empty?
    puts
    @root = prompt.ask('Path to redmine root:', required: true, default: 'redmine')
  end

  @root = File.expand_path(@root)

  unless Dir.exist?(@root)
    create_dir(@root)
  end

  logger.info("REDMINE_ROOT: #{@root}")

  unreadable_files = []
  all_directories = []

  Find.find(@root).each do |item|
    if unreadable_files.size > CHECK_N_INACCESSIBLE_FILES
      break
    end

    # Installer only need read permission for a few files
    # but for sure it checks all of them
    if !File.readable?(item)
      unreadable_files << item
      next
    end

    # Actualy this permission should not be needed
    # becase deletable is checked by parent directory
    # if !File.writable?(item)
    #   unreadable_files << item
    # end

    # Parent directory of the root can have any permission
    if item != @root
      all_directories << File.dirname(item)
    end
  end

  if unreadable_files.any?
    error "Application root contains unreadable files. Make sure that all files in #{@root} are readable for user #{env_user} (limit #{CHECK_N_INACCESSIBLE_FILES} files: #{unreadable_files.join(', ')})"
  end

  unwritable_directories = []

  all_directories.uniq!
  all_directories.each do |item|
    if !File.writable?(item)
      unwritable_directories << item
    end
  end

  if unwritable_directories.any?
    error "Application root contains unwritable directories. Make sure that all directories in #{@root} are writable for user #{env_user} (limit #{CHECK_N_INACCESSIBLE_FILES} files: #{unwritable_directories.join(', ')})"
  end
end
files_path() click to toggle source
# File lib/redmine-installer/redmine.rb, line 50
def files_path
  File.join(root, FILES_DIR)
end
gemfile_local_path() click to toggle source
# File lib/redmine-installer/redmine.rb, line 46
def gemfile_local_path
  File.join(root, 'Gemfile.local')
end
install() click to toggle source

Run install commands (command might ask for additional informations)

# File lib/redmine-installer/redmine.rb, line 197
def install
  print_title('Redmine installing')

  Dir.chdir(root) do
    # Gems can be locked on bad version
    FileUtils.rm_f('Gemfile.lock')

    # Install new gems
    bundle_install

    # Generate secret token
    rake_generate_secret_token

    # Ensuring database
    rake_db_create

    # Load database dump (if was set via CLI or attach on package)
    load_database_dump

    # Migrating
    rake_db_migrate

    # Plugin migrating
    rake_redmine_plugin_migrate

    # Install easyproject
    rake_easyproject_install if easyproject?
  end
end
load_profile(profile) click to toggle source
# File lib/redmine-installer/redmine.rb, line 78
def load_profile(profile)
  @root = profile.redmine_root if root.empty?
  @backup_type = profile.backup_type
  @backup_root = profile.backup_root || profile.backup_dir

  # Convert setting from v1
  case @backup_type
  when :full_backup
    @backup_type = :full
  when :backup, :only_database
    @backup_type = :database
  end

  # Only valid setting
  unless [:full, :database, :nothing].include?(@backup_type)
    @backup_type = nil
  end
end
log_path() click to toggle source
# File lib/redmine-installer/redmine.rb, line 62
def log_path
  File.join(root, 'log')
end
make_backup() click to toggle source

Backup:

  • full redmine (except log, tmp)

  • production database

# File lib/redmine-installer/redmine.rb, line 474
def make_backup
  print_title('Data backup')

  @backup_type ||= prompt.select('What type of backup do you want?',
    'Full (redmine root and database)' => :full,
    'Only database' => :database,
    'Nothing' => :nothing)

  logger.info("Backup type: #{@backup_type}")

  # Dangerous option
  if @backup_type == :nothing
    if prompt.yes?('Are you sure you dont want backup?', default: false)
      logger.info('Backup option nothing was confirmed')
      return
    else
      @backup_type = nil
      return make_backup
    end
  end

  @backup_root ||= prompt.ask('Where to save backup:', required: true, default: DEFAULT_BACKUP_ROOT)
  @backup_root = File.expand_path(@backup_root)

  @backup_dir = File.join(@backup_root, Time.now.strftime('backup_%d%m%Y_%H%M%S'))
  create_dir(@backup_dir)

  files_to_backup = []
  Dir.chdir(root) do
    case @backup_type
    when :full
      files_to_backup = Dir.glob(File.join('**', '{*,.*}'))
    end
  end

  if files_to_backup.any?
    files_to_backup.delete_if do |path|
      path.start_with?(*BACKUP_EXCLUDE_FILES)
    end

    @backup_package = File.join(@backup_dir, 'redmine.zip')

    Dir.chdir(root) do
      puts
      puts 'Files backuping'
      Zip::File.open(@backup_package, Zip::File::CREATE) do |zipfile|
        progressbar = TTY::ProgressBar.new(PROGRESSBAR_FORMAT, total: files_to_backup.size, frequency: 2, clear: true)

        files_to_backup.each do |entry|
          zipfile.add(entry, entry)
          progressbar.advance(1)
        end

        progressbar.finish
      end
    end

    puts "Files backed up on #{@backup_package}"
    logger.info('Files backed up')
  end

  @database = Database.init(self)
  @database.make_backup(@backup_dir)

  puts "Database backed up on #{@database.backup}"
  logger.info('Database backed up')
end
move_from(other_redmine) click to toggle source
# File lib/redmine-installer/redmine.rb, line 289
def move_from(other_redmine)
  Dir.chdir(other_redmine.root) do

    # Bundler save plugin with absolute paths
    # which is not pointing to the temporary directory
    bundle_index = File.join(Dir.pwd, '.bundle/plugin/index')

    if File.exist?(bundle_index)
      index = YAML.load_file(bundle_index)

      # { load_paths: { PLUGIN_NAME: *PATHS } }
      #
      if index.has_key?('load_paths')
        load_paths = index['load_paths']
        if load_paths.is_a?(Hash)
          load_paths.each do |_, paths|
            paths.each do |path|
              path.sub!(other_redmine.root, root)
            end
          end
        end
      end

      # { plugin_paths: { PLUGIN_NAME: PATH } }
      #
      if index.has_key?('plugin_paths')
        plugin_paths = index['plugin_paths']
        if plugin_paths.is_a?(Hash)
          plugin_paths.each do |_, path|
            path.sub!(other_redmine.root, root)
          end
        end
      end

      File.write(bundle_index, index.to_yaml)

      logger.info("Bundler plugin index from #{other_redmine.root} into #{root}")
    else
      logger.info("Bundler plugin index from #{other_redmine.root} not found")
    end

    Dir.entries('.').each do |entry|
      next if entry == '.' || entry == '..'

      if entry == FILES_DIR && task.options.copy_files_with_symlink
        FileUtils.rm(entry)
      else
        FileUtils.mv(entry, root)
      end
    end
  end

  logger.info("Copyied from #{other_redmine.root} into #{root}")
end
pids_files() click to toggle source
# File lib/redmine-installer/redmine.rb, line 70
def pids_files
  Dir.glob(File.join(root, 'tmp', 'pids', '*'))
end
plugins_path() click to toggle source
# File lib/redmine-installer/redmine.rb, line 54
def plugins_path
  File.join(root, 'plugins')
end
restore_db() click to toggle source
# File lib/redmine-installer/redmine.rb, line 251
def restore_db
  print_title('Database restoring')

  @database = Database.init(self)

  Dir.chdir(root) do
    # Load database dump (if was set via CLI)
    load_database_dump

    # Migrating
    rake_db_migrate

    # Plugin migrating
    rake_redmine_plugin_migrate

    # Install easyproject
    rake_easyproject_install if easyproject?
  end
end
running?() click to toggle source
# File lib/redmine-installer/redmine.rb, line 74
def running?
  pids_files.any?
end
save_profile(profile) click to toggle source
# File lib/redmine-installer/redmine.rb, line 97
def save_profile(profile)
  profile.redmine_root = @root
  profile.backup_type = @backup_type
  profile.backup_root = @backup_root
end
upgrade() click to toggle source
# File lib/redmine-installer/redmine.rb, line 227
def upgrade
  print_title('Redmine upgrading')

  Dir.chdir(root) do
    # Gems can be locked on bad version
    FileUtils.rm_f('Gemfile.lock')

    # Install new gems
    bundle_install

    # Generate secret token
    rake_generate_secret_token

    # Migrating
    rake_db_migrate

    # Plugin migrating
    rake_redmine_plugin_migrate

    # Install easyproject
    rake_easyproject_install if easyproject?
  end
end
valid_options() click to toggle source
# File lib/redmine-installer/redmine.rb, line 542
def valid_options
  if @database_dump_to_load && !(File.exist?(@database_dump_to_load) && File.file?(@database_dump_to_load))
    error "Database dump #{@database_dump_to_load} does not exist (path is expanded)."
  end
end
validate() click to toggle source
# File lib/redmine-installer/redmine.rb, line 453
def validate
  # Check for required files
  Dir.chdir(root) do
    REQUIRED_FILES.each do |path|
      unless File.exist?(path)
        error "Redmine #{root} is not valid. Directory '#{path}' is missing."
      end
    end
  end

  # Plugins are in right dir
  Dir.glob(File.join(root, 'vendor', 'plugins', '*')).each do |path|
    if File.directory?(path)
      error "Plugin should be on plugins dir. On vendor/plugins is #{path}"
    end
  end
end
yield_missing_plugins(source_directory, target_directory) { |expand_path, expand_path| ... } click to toggle source
# File lib/redmine-installer/redmine.rb, line 398
def yield_missing_plugins(source_directory, target_directory)
  if !Dir.exist?(source_directory)
    return
  end

  Dir.chdir(source_directory) do
    Dir.entries('.').each do |plugin|
      next if plugin == '.' || plugin == '..'

      # Plugin is not directory
      unless File.directory?(plugin)
        next
      end

      to = File.join(target_directory, plugin)

      # Plugins does not exist
      unless Dir.exist?(to)
        yield File.expand_path(plugin), File.expand_path(to)
      end
    end
  end
end

Private Instance Methods

bundle_install() click to toggle source
# File lib/redmine-installer/redmine.rb, line 553
def bundle_install
  gemfile = File.join(root, 'Gemfile')
  status = run_command("bundle install #{task.options.bundle_options} --gemfile #{gemfile}", 'Bundle install')

  # Even if bundle could not install all gem EXIT_SUCCESS is returned
  if !status || !File.exist?('Gemfile.lock')
    puts
    selected = prompt.select("Gemfile.lock wasn't created. Please choose one option:",
      'Try again' => :try_again,
      'Change bundle options' => :change_options,
      'Cancel' => :cancel)

    case selected
    when :try_again
      bundle_install
    when :change_options
      task.options.bundle_options = prompt.ask('New options:', default: task.options.bundle_options)
      bundle_install
    when :cancel
      error('Operation canceled by user')
    end
  end
end
easyproject?() click to toggle source
# File lib/redmine-installer/redmine.rb, line 670
def easyproject?
  Dir.entries(plugins_path).include?('easyproject')
end
load_database_dump() click to toggle source
# File lib/redmine-installer/redmine.rb, line 696
def load_database_dump
  if @database_dump_to_load
    load_database_dump_from_file
  elsif task.package_config.dump_attached? && task.package_config.dump_compatible?(@database)
    load_database_dump_from_package
  end
end
load_database_dump_from_file() click to toggle source
# File lib/redmine-installer/redmine.rb, line 674
def load_database_dump_from_file
  selected = prompt.select('Database dump will be loaded. Before that all data must be destroy.',
    'Skip dump loading' => :cancel,
    'I am aware of this. Want to continue' => :continue)

  if selected == :continue
    @database.do_restore(@database_dump_to_load)
    logger.info('Database dump was loaded.')
  else
    logger.info('Database dump loading was skipped.')
  end
end
load_database_dump_from_package() click to toggle source
# File lib/redmine-installer/redmine.rb, line 687
def load_database_dump_from_package
  if !prompt.no?('Would you like to load default data? Warning: By choosing "yes", you are confirming that all your existing redmine data will be removed.')

    @database.do_restore(task.package_config.sql_dump_file)

    logger.info('Default database dump was loaded.')
  end
end
rake_db_create() click to toggle source
# File lib/redmine-installer/redmine.rb, line 577
def rake_db_create
  # Always return 0
  run_command('RAILS_ENV=production bundle exec rake db:create', 'Database creating')
end
rake_db_migrate() click to toggle source
# File lib/redmine-installer/redmine.rb, line 582
def rake_db_migrate
  status = run_command('RAILS_ENV=production bundle exec rake db:migrate', 'Database migrating')

  unless status
    puts
    selected = prompt.select('Migration end with error. Please choose one option:',
      'Try again' => :try_again,
      'Create database first' => :create_database,
      'Change database configuration' => :change_configuration,
      'Cancel' => :cancel)

    case selected
    when :try_again
      rake_db_migrate
    when :create_database
      rake_db_create
      rake_db_migrate
    when :change_configuration
      create_database_yml
      rake_db_migrate
    when :cancel
      error('Operation canceled by user')
    end
  end
end
rake_easyproject_install() click to toggle source
# File lib/redmine-installer/redmine.rb, line 650
def rake_easyproject_install
  status = without_env('NAME') {
    run_command('RAILS_ENV=production bundle exec rake easyproject:install', 'Installing easyproject')
  }

  unless status
    puts
    selected = prompt.select('Easyproject could not be installed. Please choose one option:',
      'Try again' => :try_again,
      'Cancel' => :cancel)

    case selected
    when :try_again
      rake_easyproject_install
    when :cancel
      error('Operation canceled by user')
    end
  end
end
rake_generate_secret_token() click to toggle source
# File lib/redmine-installer/redmine.rb, line 629
def rake_generate_secret_token
  status = run_command('RAILS_ENV=production bundle exec rake generate_secret_token', 'Generating secret token')

  unless status
    puts
    selected = prompt.select('Secret token could not be created. Please choose one option:',
      'Try again' => :try_again,
      'Continue' => :continue,
      'Cancel' => :cancel)

    case selected
    when :try_again
      rake_generate_secret_token
    when :continue
      logger.warn('Secret token could not be created but step was skipped.')
    when :cancel
      error('Operation canceled by user')
    end
  end
end
rake_redmine_plugin_migrate() click to toggle source
# File lib/redmine-installer/redmine.rb, line 608
def rake_redmine_plugin_migrate
  status = run_command('RAILS_ENV=production bundle exec rake redmine:plugins:migrate', 'Plugins migration')

  unless status
    puts
    selected = prompt.select('Plugin migration end with error. Please choose one option:',
      'Try again' => :try_again,
      'Continue' => :continue,
      'Cancel' => :cancel)

    case selected
    when :try_again
      rake_redmine_plugin_migrate
    when :continue
      logger.warn('Plugin migration end with error but step was skipped.')
    when :cancel
      error('Operation canceled by user')
    end
  end
end
without_env(*names) { || ... } click to toggle source
# File lib/redmine-installer/redmine.rb, line 704
def without_env(*names)
  backup = ENV.clone.to_hash
  ENV.delete_if {|key, _| names.include?(key) }
  yield
ensure
  ENV.replace(backup)
end