redmine-installer 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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