redmine-installer 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +25 -0
  3. data/Gemfile +19 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +273 -0
  6. data/Rakefile +7 -0
  7. data/bin/redmine +8 -0
  8. data/lib/redmine-installer.rb +72 -0
  9. data/lib/redmine-installer/backup.rb +48 -0
  10. data/lib/redmine-installer/cli.rb +90 -0
  11. data/lib/redmine-installer/command.rb +142 -0
  12. data/lib/redmine-installer/config_param.rb +90 -0
  13. data/lib/redmine-installer/error.rb +5 -0
  14. data/lib/redmine-installer/exec.rb +158 -0
  15. data/lib/redmine-installer/ext/module.rb +7 -0
  16. data/lib/redmine-installer/ext/string.rb +15 -0
  17. data/lib/redmine-installer/git.rb +51 -0
  18. data/lib/redmine-installer/helper.rb +5 -0
  19. data/lib/redmine-installer/helpers/generate_config.rb +29 -0
  20. data/lib/redmine-installer/install.rb +56 -0
  21. data/lib/redmine-installer/locales/cs.yml +145 -0
  22. data/lib/redmine-installer/locales/en.yml +146 -0
  23. data/lib/redmine-installer/plugin.rb +9 -0
  24. data/lib/redmine-installer/plugins/base.rb +28 -0
  25. data/lib/redmine-installer/plugins/database.rb +168 -0
  26. data/lib/redmine-installer/plugins/email_sending.rb +82 -0
  27. data/lib/redmine-installer/plugins/redmine_plugin.rb +30 -0
  28. data/lib/redmine-installer/plugins/web_server.rb +26 -0
  29. data/lib/redmine-installer/profile.rb +74 -0
  30. data/lib/redmine-installer/step.rb +15 -0
  31. data/lib/redmine-installer/steps/backup.rb +120 -0
  32. data/lib/redmine-installer/steps/base.rb +60 -0
  33. data/lib/redmine-installer/steps/database_config.rb +15 -0
  34. data/lib/redmine-installer/steps/email_config.rb +11 -0
  35. data/lib/redmine-installer/steps/install.rb +21 -0
  36. data/lib/redmine-installer/steps/load_package.rb +180 -0
  37. data/lib/redmine-installer/steps/move_redmine.rb +22 -0
  38. data/lib/redmine-installer/steps/redmine_root.rb +29 -0
  39. data/lib/redmine-installer/steps/upgrade.rb +55 -0
  40. data/lib/redmine-installer/steps/validation.rb +38 -0
  41. data/lib/redmine-installer/steps/webserver_config.rb +22 -0
  42. data/lib/redmine-installer/task.rb +85 -0
  43. data/lib/redmine-installer/upgrade.rb +68 -0
  44. data/lib/redmine-installer/utils.rb +233 -0
  45. data/lib/redmine-installer/version.rb +5 -0
  46. data/redmine-installer.gemspec +25 -0
  47. data/spec/lib/install_spec.rb +46 -0
  48. data/spec/lib/upgrade_spec.rb +62 -0
  49. data/spec/load_redmine.rb +24 -0
  50. data/spec/spec_helper.rb +30 -0
  51. metadata +130 -0
@@ -0,0 +1,9 @@
1
+ module Redmine::Installer
2
+ module Plugin
3
+ autoload :Base, 'redmine-installer/plugins/base'
4
+ autoload :Database, 'redmine-installer/plugins/database'
5
+ autoload :EmailSending, 'redmine-installer/plugins/email_sending'
6
+ autoload :WebServer, 'redmine-installer/plugins/web_server'
7
+ autoload :RedminePlugin, 'redmine-installer/plugins/redmine_plugin'
8
+ end
9
+ end
@@ -0,0 +1,28 @@
1
+ module Redmine::Installer::Plugin
2
+ class Base
3
+
4
+ include Redmine::Installer::Utils
5
+
6
+ # Register children
7
+ def self.inherited(child)
8
+ all << child
9
+ end
10
+
11
+ def self.all
12
+ unless self.instance_variable_defined?(:@all)
13
+ self.instance_variable_set(:@all, Array.new)
14
+ end
15
+
16
+ self.instance_variable_get(:@all)
17
+ end
18
+
19
+ def self.title
20
+ translate("plugin.#{self.superclass.class_name.downcase}.#{self.class_name.downcase}.title")
21
+ end
22
+
23
+ # def self.plugin_name
24
+ # binding.pry unless @__binding
25
+ # end
26
+
27
+ end
28
+ end
@@ -0,0 +1,168 @@
1
+ require 'yaml'
2
+
3
+ module Redmine::Installer::Plugin
4
+ class Database < Base
5
+
6
+ DATABASE_YML_PATH = 'config/database.yml'
7
+ DATABASE_BACKUP_DIR = '__database'
8
+
9
+ attr_reader :params
10
+
11
+ def self.load_all(redmine_root)
12
+ database_file = File.join(redmine_root, DATABASE_YML_PATH)
13
+ return [] unless File.exist?(database_file)
14
+
15
+ to_return = []
16
+ definitions = YAML.load_file(database_file)
17
+ definitions.each do |name, data|
18
+
19
+ klass = all.detect{|klass| klass.adapter_name == data['adapter']}
20
+
21
+ next if klass.nil?
22
+
23
+ klass = klass.new
24
+ klass.load(data)
25
+
26
+ to_return << klass
27
+ end
28
+ to_return
29
+ end
30
+
31
+ def self.backup_all(redmine_root, backup_dir)
32
+ load_all(redmine_root).each do |klass|
33
+ klass.backup(backup_dir)
34
+ end
35
+ end
36
+
37
+ def self.restore_all(redmine_root, backup_dir)
38
+ load_all(redmine_root).each do |klass|
39
+ klass.restore(backup_dir)
40
+ end
41
+ end
42
+
43
+ def initialize
44
+ @params = Redmine::Installer::ConfigParams.new
45
+ @params.add('database')
46
+ @params.add('host').default('localhost')
47
+ @params.add('username')
48
+ @params.add('password')
49
+ @params.add('encoding').default('utf8')
50
+ end
51
+
52
+ # Transform ConfigParams into rails database.yml structure.
53
+ # Method creates production and developemtn environemnt
54
+ # with the same parameters.
55
+ def build
56
+ data = Hash[@params.map{|p| [p.name, p.value]}]
57
+ data['adapter'] = self.class.adapter_name
58
+ data = {
59
+ 'production' => data,
60
+ 'development' => data,
61
+ }
62
+ data
63
+ end
64
+
65
+ # Load paramaters for connection
66
+ def load(data)
67
+ data.each do |name, value|
68
+ # Get param
69
+ param = @params[name]
70
+
71
+ # Unsupported key or unnecessary parameter
72
+ next if param.nil?
73
+
74
+ # Save value
75
+ param.value = value
76
+ end
77
+ end
78
+
79
+ def make_config(redmine_root)
80
+ File.open(File.join(redmine_root, DATABASE_YML_PATH), 'w') do |f|
81
+ f.puts(YAML.dump(build))
82
+ end
83
+ end
84
+
85
+ def file_for_backup(dir)
86
+ FileUtils.mkdir_p(File.join(dir, DATABASE_BACKUP_DIR))
87
+ File.join(dir, DATABASE_BACKUP_DIR, "#{self.class.adapter_name}.#{params['database'].value}.dump")
88
+ end
89
+
90
+ def backup(dir)
91
+ file = file_for_backup(dir)
92
+
93
+ # More enviroments can use the same database
94
+ return if File.exist?(file)
95
+
96
+ Kernel.system(command_for_backup(file))
97
+ end
98
+
99
+ def restore(dir)
100
+ file = file_for_backup(dir)
101
+
102
+ # More enviroments can use the same database
103
+ return unless File.exist?(file)
104
+
105
+ Kernel.system(command_for_restore(file))
106
+ end
107
+
108
+ # Get valu from param
109
+ def get(name)
110
+ params[name].value
111
+ end
112
+
113
+
114
+ # =========================================================================
115
+ # MySQL
116
+
117
+ class MySQL < Database
118
+ def self.adapter_name
119
+ 'mysql2'
120
+ end
121
+
122
+ def initialize
123
+ super
124
+ @params.add('port').default(3306)
125
+ end
126
+
127
+ def command_args
128
+ "-h #{params['host'].value} -P #{get('port')} -u #{get('username')} -p#{get('password')} #{get('database')}"
129
+ end
130
+
131
+ def command_for_backup(file)
132
+ "mysqldump --add-drop-database #{command_args} > #{file}"
133
+ end
134
+
135
+ def command_for_restore(file)
136
+ "mysql #{command_args} < #{file}"
137
+ end
138
+ end
139
+
140
+
141
+ # =========================================================================
142
+ # PostgreSQL
143
+
144
+ class PostgreSQL < Database
145
+ def self.adapter_name
146
+ 'pg'
147
+ end
148
+
149
+ def initialize
150
+ super
151
+ @params.add('port').default(5432)
152
+ end
153
+
154
+ def command(comm, file)
155
+ %{PGPASSWORD="#{get('password')}" #{comm} -i -h #{get('host')} -p #{get('port')} -U #{get('username')} -Fc -f #{file}}
156
+ end
157
+
158
+ def command_for_backup(file)
159
+ command('pg_dump', file)
160
+ end
161
+
162
+ def command_for_restore(file)
163
+ command('psql', file)
164
+ end
165
+ end
166
+
167
+ end
168
+ end
@@ -0,0 +1,82 @@
1
+ module Redmine::Installer::Plugin
2
+ class EmailSending < Base
3
+
4
+ CONFIGURATION_YML_PATH = 'config/configuration.yml'
5
+
6
+ attr_reader :params
7
+
8
+ def initialize
9
+ @params = Redmine::Installer::ConfigParams.new
10
+ @params.add('user_name')
11
+ @params.add('password')
12
+ end
13
+
14
+ def build
15
+ {
16
+ 'default' => {
17
+ 'email_delivery' => {
18
+ 'delivery_method' => delivery_method,
19
+ "#{delivery_method}_settings" => delivery_settings
20
+ }
21
+ }
22
+ }
23
+ end
24
+
25
+ def make_config(redmine_root)
26
+ File.open(File.join(redmine_root, CONFIGURATION_YML_PATH), 'w') do |f|
27
+ f.puts(YAML.dump(build))
28
+ end
29
+ end
30
+
31
+ def delivery_method
32
+ :smtp
33
+ end
34
+
35
+ # Build ConfigParams
36
+ def delivery_settings
37
+ settings = {}
38
+ @params.each do |param|
39
+ next if param.value.empty?
40
+ settings[param.name] = param.value
41
+ end
42
+ settings
43
+ end
44
+ end
45
+
46
+ class Gmail < EmailSending
47
+ def delivery_settings
48
+ super.merge({
49
+ 'enable_starttls_auto' => true,
50
+ 'address' => 'smtp.gmail.com',
51
+ 'port' => 587,
52
+ 'domain' => 'smtp.gmail.com',
53
+ 'authentication' => :plain
54
+ })
55
+ end
56
+ end
57
+
58
+ class SendMail < EmailSending
59
+ def initialize
60
+ @params = Redmine::Installer::ConfigParams.new
61
+ @params.add('location').default('/usr/sbin/sendmail')
62
+ @params.add('arguments').default('-i -t')
63
+ end
64
+
65
+ def delivery_method
66
+ :sendmail
67
+ end
68
+ end
69
+
70
+ class SMTPFromScratch < EmailSending
71
+ def initialize
72
+ @params = Redmine::Installer::ConfigParams.new
73
+ @params.add('address')
74
+ @params.add('port').default(587)
75
+ @params.add('domain')
76
+ @params.add('user_name')
77
+ @params.add('password')
78
+ @params.add('authentication')
79
+ @params.add('enable_starttls_auto')
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,30 @@
1
+ module Redmine::Installer::Plugin
2
+ class RedminePlugin < Base
3
+
4
+ # Search plugins folder
5
+ def self.am_i_there?
6
+ records = Dir.glob(File.join('plugins', '*'))
7
+ records.map! do |record|
8
+ File.basename(record)
9
+ end
10
+
11
+ records.include?(self.class_name.downcase)
12
+ end
13
+
14
+ class EasyProject < RedminePlugin
15
+
16
+ RAKE_EASYPROJECT_INSTALL = 'bundle exec rake easyproject:install RAILS_ENV=production'
17
+
18
+ def self.install
19
+ if am_i_there?
20
+ run_command(RAKE_EASYPROJECT_INSTALL, t('plugin.redmine_plugin.easyproject.install'))
21
+ end
22
+ end
23
+
24
+ def self.upgrade
25
+ install
26
+ end
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,26 @@
1
+ module Redmine::Installer::Plugin
2
+ class WebServer < Base
3
+
4
+ # Generate config based on class and redmine_root.
5
+ # Texts are in lang files.
6
+ def self.generate_config(redmine_root)
7
+ translate("plugin.#{self.superclass.class_name.downcase}.#{self.class_name.downcase}.configuration", redmine_root: redmine_root)
8
+ end
9
+
10
+ class Webrick < WebServer
11
+ end
12
+
13
+ class Thin < WebServer
14
+ end
15
+
16
+ class ApachePassenger < WebServer
17
+ end
18
+
19
+ class NginxPassenger < WebServer
20
+ end
21
+
22
+ class Puma < WebServer
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,74 @@
1
+ require 'fileutils'
2
+ require 'yaml'
3
+
4
+ module Redmine::Installer
5
+ class Profile
6
+
7
+ include Redmine::Installer::Utils
8
+
9
+ CONFIG_FILE = File.join(Dir.home, '.redmine-installer-profiles.yml')
10
+
11
+ def self.save(task)
12
+ return unless check_writable
13
+ return unless confirm(:do_you_want_save_step_for_further_use, true)
14
+
15
+ profile = Profile.new(task)
16
+ profile.save
17
+
18
+ say t(:your_profile_can_be_used_as, id: profile.id), 2
19
+ end
20
+
21
+ def self.load(task, id)
22
+ profile = Profile.new(task)
23
+ profile.load(id)
24
+ end
25
+
26
+ def self.check_writable
27
+ FileUtils.touch(CONFIG_FILE)
28
+ File.writable?(CONFIG_FILE)
29
+ end
30
+
31
+ attr_accessor :task
32
+
33
+ def initialize(task)
34
+ self.task = task
35
+
36
+ # Load profiles
37
+ @data = YAML.load_file(CONFIG_FILE) rescue nil
38
+
39
+ # Make empty Hash if there is no profiles
40
+ @data = {} unless @data.is_a?(Hash)
41
+ end
42
+
43
+ def id
44
+ @id ||= @data.keys.map(&:to_i).max.to_i + 1
45
+ end
46
+
47
+ def save
48
+ # All steps save configuration which can be use again
49
+ configuration = {}
50
+ task.steps.each do |_, step|
51
+ step.save(configuration)
52
+ end
53
+
54
+ @data[id] = configuration
55
+
56
+ File.open(CONFIG_FILE, 'w') {|f| f.puts(YAML.dump(@data))}
57
+ end
58
+
59
+ def load(id)
60
+ @id = id.to_i
61
+
62
+ configuration = @data[@id]
63
+
64
+ return {} if configuration.nil?
65
+
66
+ task.steps.each do |_, step|
67
+ step.load(configuration)
68
+ end
69
+
70
+ return configuration
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,15 @@
1
+ module Redmine::Installer
2
+ module Step
3
+ autoload :Base, 'redmine-installer/steps/base'
4
+ autoload :LoadPackage, 'redmine-installer/steps/load_package'
5
+ autoload :DatabaseConfig, 'redmine-installer/steps/database_config'
6
+ autoload :EmailConfig, 'redmine-installer/steps/email_config'
7
+ autoload :Install, 'redmine-installer/steps/install'
8
+ autoload :MoveRedmine, 'redmine-installer/steps/move_redmine'
9
+ autoload :WebserverConfig, 'redmine-installer/steps/webserver_config'
10
+ autoload :Validation, 'redmine-installer/steps/validation'
11
+ autoload :Backup, 'redmine-installer/steps/backup'
12
+ autoload :Upgrade, 'redmine-installer/steps/upgrade'
13
+ autoload :RedmineRoot, 'redmine-installer/steps/redmine_root'
14
+ end
15
+ end
@@ -0,0 +1,120 @@
1
+ require 'fileutils'
2
+ require 'zip'
3
+
4
+ module Redmine::Installer::Step
5
+ class Backup < Base
6
+
7
+ DEFAULT_BACKUP_DIR = File.join(Dir.home, 'redmine-backups')
8
+
9
+ def up
10
+ choices = {}
11
+ choices[:full_backup] = t(:full_backup)
12
+ choices[:backup] = t(:backup)
13
+ choices[:only_database] = t(:only_database)
14
+ choices[:skip] = t(:skip)
15
+
16
+ @backup_type ||= choose(:do_you_want_backup_redmine, choices, default: :backup)
17
+
18
+ case @backup_type
19
+ when :full_backup
20
+ do_full_backup
21
+ when :backup
22
+ do_backup
23
+ when :only_database
24
+ database_backup
25
+ end
26
+ end
27
+
28
+ def down
29
+ database_restore if @database_backed_up
30
+ end
31
+
32
+ def final_step
33
+ if @current_backup_dir
34
+ say t(:backup_is_stored_at, dir: @current_backup_dir), 2
35
+ end
36
+ end
37
+
38
+ def save(configuration)
39
+ configuration['backup_type'] = @backup_type
40
+ configuration['backup_dir'] = @backup_dir
41
+ end
42
+
43
+ def load(configuration)
44
+ @backup_type = configuration['backup_type']
45
+ @backup_dir = configuration['backup_dir']
46
+ end
47
+
48
+ private
49
+
50
+ def check_backup_dir
51
+ if @backup_dir.nil?
52
+ dir = ask(:what_dir_for_backups, default: DEFAULT_BACKUP_DIR)
53
+ dir = File.expand_path(dir)
54
+
55
+ try_create_dir(dir) unless Dir.exist?(dir)
56
+
57
+ @backup_dir = dir
58
+ end
59
+ end
60
+
61
+ def create_current_backup_dir
62
+ return if @current_backup_dir
63
+
64
+ check_backup_dir
65
+ @current_backup_dir = File.join(@backup_dir, Time.now.strftime('backup_%d%m%Y_%H%M%S'))
66
+ try_create_dir(@current_backup_dir)
67
+ end
68
+
69
+ def database_backup
70
+ create_current_backup_dir
71
+
72
+ plugin::Database.backup_all(base.redmine_root, @current_backup_dir)
73
+ @database_backed_up = true
74
+ end
75
+
76
+ def database_restore
77
+ plugin::Database.restore_all(base.redmine_root, @current_backup_dir)
78
+ end
79
+
80
+ def do_full_backup
81
+ create_current_backup_dir
82
+
83
+ zipfile_name = File.join(@current_backup_dir, 'redmine.zip')
84
+
85
+ Dir.chdir(base.redmine_root) do
86
+ Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
87
+ Dir.glob(File.join('**', '{*,.*}')).each do |entry|
88
+ next if entry.end_with?('.') || entry.end_with?('..')
89
+
90
+ zipfile.add(entry, entry)
91
+ end
92
+ end
93
+ end
94
+
95
+ database_backup
96
+ end
97
+
98
+ def do_backup
99
+ create_current_backup_dir
100
+
101
+ # create config dir
102
+ config_dir = File.join(@current_backup_dir, 'config')
103
+ FileUtils.mkdir(config_dir)
104
+
105
+ # database.yml
106
+ database_file = File.join(base.redmine_root, plugin::Database::DATABASE_YML_PATH)
107
+ FileUtils.cp(database_file, config_dir) if File.exist?(database_file)
108
+
109
+ # configuration.yml
110
+ configuration_file = File.join(base.redmine_root, plugin::EmailSending::CONFIGURATION_YML_PATH)
111
+ FileUtils.cp(configuration_file, config_dir) if File.exist?(configuration_file)
112
+
113
+ # files
114
+ FileUtils.cp_r(File.join(base.redmine_root, 'files'), @current_backup_dir)
115
+
116
+ database_backup
117
+ end
118
+
119
+ end
120
+ end