redmine-installer 1.0.7 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.travis.yml +7 -0
  4. data/Gemfile +3 -15
  5. data/README.md +49 -177
  6. data/bin/redmine +4 -5
  7. data/lib/redmine-installer/backup.rb +13 -40
  8. data/lib/redmine-installer/cli.rb +92 -61
  9. data/lib/redmine-installer/command.rb +63 -117
  10. data/lib/redmine-installer/configuration.rb +148 -0
  11. data/lib/redmine-installer/database.rb +204 -0
  12. data/lib/redmine-installer/environment.rb +21 -0
  13. data/lib/redmine-installer/errors.rb +7 -0
  14. data/lib/redmine-installer/install.rb +37 -42
  15. data/lib/redmine-installer/logger.rb +83 -0
  16. data/lib/redmine-installer/package.rb +180 -0
  17. data/lib/redmine-installer/patches/ruby.rb +35 -0
  18. data/lib/redmine-installer/patches/tty.rb +16 -0
  19. data/lib/redmine-installer/profile.rb +24 -55
  20. data/lib/redmine-installer/redmine.rb +551 -0
  21. data/lib/redmine-installer/spec/spec.rb +81 -0
  22. data/lib/redmine-installer/task.rb +18 -77
  23. data/lib/redmine-installer/task_module.rb +12 -0
  24. data/lib/redmine-installer/upgrade.rb +51 -59
  25. data/lib/redmine-installer/utils.rb +46 -233
  26. data/lib/redmine-installer/version.rb +2 -4
  27. data/lib/redmine-installer.rb +69 -56
  28. data/redmine-installer.gemspec +20 -19
  29. data/spec/custom_matchers.rb +21 -0
  30. data/spec/installer_helper.rb +107 -0
  31. data/spec/installer_process.rb +82 -0
  32. data/spec/lib/backup_restore_spec.rb +81 -0
  33. data/spec/lib/install_spec.rb +125 -36
  34. data/spec/lib/upgrade_spec.rb +73 -52
  35. data/spec/packages/redmine-3.1.0.zip +0 -0
  36. data/spec/packages/redmine-3.2.0.zip +0 -0
  37. data/spec/packages/redmine-3.3.0-bad-migration.zip +0 -0
  38. data/spec/packages/redmine-3.3.0.zip +0 -0
  39. data/spec/packages/something-else.zip +0 -0
  40. data/spec/packages_helper.rb +19 -0
  41. data/spec/shared_contexts.rb +13 -0
  42. data/spec/spec_helper.rb +34 -18
  43. metadata +62 -63
  44. data/lib/redmine-installer/config_param.rb +0 -100
  45. data/lib/redmine-installer/error.rb +0 -5
  46. data/lib/redmine-installer/exec.rb +0 -158
  47. data/lib/redmine-installer/ext/module.rb +0 -7
  48. data/lib/redmine-installer/ext/string.rb +0 -15
  49. data/lib/redmine-installer/git.rb +0 -51
  50. data/lib/redmine-installer/helper.rb +0 -5
  51. data/lib/redmine-installer/helpers/generate_config.rb +0 -29
  52. data/lib/redmine-installer/locales/cs.yml +0 -147
  53. data/lib/redmine-installer/locales/en.yml +0 -154
  54. data/lib/redmine-installer/plugin.rb +0 -9
  55. data/lib/redmine-installer/plugins/base.rb +0 -24
  56. data/lib/redmine-installer/plugins/database.rb +0 -180
  57. data/lib/redmine-installer/plugins/email_sending.rb +0 -82
  58. data/lib/redmine-installer/plugins/redmine_plugin.rb +0 -82
  59. data/lib/redmine-installer/plugins/web_server.rb +0 -26
  60. data/lib/redmine-installer/step.rb +0 -16
  61. data/lib/redmine-installer/steps/backup.rb +0 -125
  62. data/lib/redmine-installer/steps/base.rb +0 -79
  63. data/lib/redmine-installer/steps/database_config.rb +0 -15
  64. data/lib/redmine-installer/steps/email_config.rb +0 -11
  65. data/lib/redmine-installer/steps/env_check.rb +0 -20
  66. data/lib/redmine-installer/steps/install.rb +0 -23
  67. data/lib/redmine-installer/steps/load_package.rb +0 -226
  68. data/lib/redmine-installer/steps/move_redmine.rb +0 -22
  69. data/lib/redmine-installer/steps/redmine_root.rb +0 -52
  70. data/lib/redmine-installer/steps/upgrade.rb +0 -57
  71. data/lib/redmine-installer/steps/validation.rb +0 -38
  72. data/lib/redmine-installer/steps/webserver_config.rb +0 -22
  73. data/spec/load_redmine.rb +0 -24
@@ -0,0 +1,81 @@
1
+ # $stdin.sync = true
2
+ # $stdout.sync = true
3
+
4
+ # # require 'redmine-installer/spec/cursor'
5
+
6
+ # class TestPrompt < TTY::Prompt
7
+
8
+ # def initialize(*args)
9
+ # # @input = StringIO.new
10
+ # # @output = StringIO.new
11
+ # # super(input: @input, output: @output)
12
+ # super
13
+ # @pastel = Pastel.new(enabled: false)
14
+ # end
15
+
16
+ # end
17
+
18
+ # module RedmineInstaller
19
+
20
+ # # def self.prompt
21
+ # # @prompt ||= TestPrompt.new
22
+ # # end
23
+
24
+ # def self.pastel
25
+ # @pastel ||= Pastel.new(enabled: false)
26
+ # end
27
+
28
+ # end
29
+
30
+ # module TTY
31
+ # module Cursor
32
+
33
+ # singleton_methods.each do |name|
34
+ # class_eval <<-METHODS
35
+
36
+ # def #{name}(*)
37
+ # ''
38
+ # end
39
+
40
+ # def self.#{name}(*)
41
+ # ''
42
+ # end
43
+
44
+ # METHODS
45
+ # end
46
+
47
+ # end
48
+ # end
49
+
50
+ $stdin.sync = true
51
+ $stdout.sync = true
52
+
53
+ module TTY::Cursor
54
+
55
+ singleton_methods.each do |name|
56
+ class_eval <<-METHODS
57
+
58
+ def #{name}(*)
59
+ ''
60
+ end
61
+
62
+ def self.#{name}(*)
63
+ ''
64
+ end
65
+
66
+ METHODS
67
+ end
68
+
69
+ end
70
+
71
+ class TestPrompt < TTY::Prompt
72
+
73
+ def initialize(*args)
74
+ super
75
+ @pastel = Pastel.new(enabled: false)
76
+ end
77
+
78
+ end
79
+
80
+ RedmineInstaller.instance_variable_set(:@prompt, TestPrompt.new)
81
+ RedmineInstaller.instance_variable_set(:@pastel, Pastel.new(enabled: false))
@@ -1,94 +1,35 @@
1
- ##
2
- # Parent for all commands (Install, Upgrade, Backup)
3
- #
4
- module Redmine::Installer
1
+ module RedmineInstaller
5
2
  class Task
3
+ include Utils
6
4
 
7
- attr_accessor :redmine_root
8
- attr_accessor :tmp_redmine_root
9
- attr_accessor :options
10
- attr_accessor :settings
11
- attr_accessor :env
5
+ attr_reader :options
12
6
 
13
- attr_reader :steps
7
+ def initialize(**options)
8
+ @options = OpenStruct.new(options)
14
9
 
15
- def initialize(options={})
16
- self.options = options
17
- self.settings = {}
18
- self.env = options[:env]
19
-
20
- # Initialize steps for task
21
- @steps = {}
22
- index = 1
23
- self.class::STEPS.each do |step|
24
- @steps[index] = step.new(index, self)
25
- index += 1
26
- end
10
+ logger.info "#{class_name} initialized with #{options}"
11
+ logger.info "RUBY_VERSION: #{RUBY_VERSION}"
12
+ logger.info "USER: #{env_user}"
27
13
  end
28
14
 
29
15
  def run
30
- @steps.each do |_, step|
31
- step.prepare
32
- end
33
-
34
- @steps.each do |_, step|
35
- step.print_title
36
- step.print_header
37
- step.up
38
- step.print_footer
39
- step.ran = true
40
- puts
41
- end
42
-
43
- @steps.each do |_, step|
44
- step.final
45
- end
16
+ up
17
+ rescue => e
18
+ @error = e
46
19
 
47
- Dir.chdir(redmine_root) do
48
- Redmine::Installer::Plugin::RedminePlugin.all.each do |plugin|
49
- plugin.final(self)
50
- end
51
- end
52
- rescue Redmine::Installer::Error => e
53
- # Rescue from error comes from installer
54
- # run steps again for cleaning
55
- @steps.values.reverse.each do |step|
56
- next unless step.ran
57
- step.down
58
- end
20
+ logger.error(e.message)
21
+ logger.error(*e.backtrace)
59
22
 
60
- $stderr.puts(ANSI.red, e.message, ANSI.clear)
61
- $stderr.flush
62
- exit(1)
63
- end
23
+ puts pastel.red(e.message)
64
24
 
65
- # Package is required for install task and
66
- # upgrade with source file
67
- def check_package
68
- if package.nil?
69
- raise Redmine::Installer::Error, I18n.translate(:error_argument_package_is_missing)
70
- end
25
+ down
71
26
  end
72
27
 
73
- def self.step
74
- Redmine::Installer::Step
28
+ def up
29
+ raise NotImplementedError
75
30
  end
76
31
 
77
- # Creating methods for recognition type of task
78
- #
79
- # == Examples:
80
- # class Install < Task
81
- # end
82
- #
83
- # Install.new.install? #=> true
84
- #
85
- def self.inherited(child)
86
- method_name = "#{child.class_name.downcase}?".to_sym
87
-
88
- define_method(method_name) { false }
89
- child.send(:define_method, method_name) { true }
90
-
91
- super
32
+ def down
92
33
  end
93
34
 
94
35
  end
@@ -0,0 +1,12 @@
1
+ module RedmineInstaller
2
+ class TaskModule
3
+ include Utils
4
+
5
+ attr_reader :task
6
+
7
+ def initialize(task)
8
+ @task = task
9
+ end
10
+
11
+ end
12
+ end
@@ -1,68 +1,60 @@
1
- ##
2
- # Backup redmine
3
- #
4
- # You can upgrade current redmine by archive or currently defined git repository.
5
- # If your redmine contain plugins which are not part of new package - all these
6
- # plugins will be kept otherwise are replaced with those from package.
7
- #
8
- # Final step will ask you if you want save steps configuration.
9
- # If you say YES, configuration will be stored as profile so next time
10
- # you can upgrade redmine faster.
11
- #
12
- # redmine upgrade PACKAGE --profile PROFILE_ID
13
- # Profiles are stored on HOME_FOLDER/.redmine-installer-profiles.yml.
14
- #
15
- # == Steps:
16
- # 1. Redmine root - where should be new redmine located
17
- # 2. Load package - extract package
18
- # 3. Validation - current redmine should be valid
19
- # 4. Backup - backup current redmine (see backup section)
20
- # 5. Upgrading - install commands are executed
21
- # 6. Moving redmine - redmine is moved from temporarily folder to given redmine_root
22
- # 7. Profile saving - generating profile (see profile section)
23
- #
24
- # == Usage:
25
- #
26
- # From archive::
27
- # # minimal
28
- # redmine upgrade PATH_TO_PACKAGE
29
- #
30
- # # full
31
- # redmine upgrade PATH_TO_PACKAGE --env ENV1,ENV2,ENV3
32
- #
33
- # From git::
34
- # # minimal
35
- # redmine upgrade --source git
36
- #
37
- # # full
38
- # redmine upgrade --source git --env ENV1,ENV2,ENV3
39
- #
40
- module Redmine::Installer
41
- class Upgrade < Task
1
+ module RedmineInstaller
2
+ class Upgrade < Install
42
3
 
43
- STEPS = [
44
- step::EnvCheck,
45
- step::RedmineRoot,
46
- step::LoadPackage,
47
- step::Validation,
48
- step::Backup,
49
- step::Upgrade,
50
- step::MoveRedmine
51
- ]
4
+ def up
5
+ if options.profile
6
+ @profile = Profile.get!(options.profile)
7
+ end
52
8
 
53
- attr_accessor :package
9
+ if @profile
10
+ @target_redmine.load_profile(@profile)
11
+ end
54
12
 
55
- def initialize(package, options={})
56
- self.package = package
57
- super(options)
13
+ @environment.check
14
+ @target_redmine.ensure_and_valid_root
15
+ @target_redmine.validate
16
+ @target_redmine.check_running_state
58
17
 
59
- check_package if options[:source] == 'file'
18
+ @package.ensure_and_valid_package
19
+ @package.extract
20
+
21
+ @temp_redmine.root = @package.redmine_root
22
+
23
+ @target_redmine.make_backup
24
+
25
+ @temp_redmine.copy_importants_from(@target_redmine)
26
+ @temp_redmine.copy_missing_plugins_from(@target_redmine)
27
+
28
+ @temp_redmine.upgrade
29
+
30
+ print_title('Finishing installation')
31
+ ok('Cleaning root'){ @target_redmine.delete_root }
32
+ ok('Moving redmine to target directory'){ @target_redmine.move_from(@temp_redmine) }
33
+ ok('Cleanning up'){ @package.clean_up }
34
+ ok('Moving installer log'){ logger.move_to(@target_redmine, suffix: 'upgrade') }
35
+
36
+ puts
37
+ puts pastel.bold('Redmine was upgraded')
38
+ logger.info('Redmine was upgraded')
39
+
40
+ if @profile.nil? && prompt.yes?('Do you want save steps for further use?', default: false)
41
+ profile = Profile.new
42
+ @target_redmine.save_profile(profile)
43
+ profile.save
44
+ end
60
45
  end
61
46
 
62
- def run
63
- Redmine::Installer::Profile.load(self, options[:profile])
64
- super
65
- Redmine::Installer::Profile.save(self) if options[:profile].nil?
47
+ def down
48
+ @temp_redmine.clean_up
49
+ @package.clean_up
50
+
51
+ if @target_redmine.database && @target_redmine.database.backuped?
52
+ puts
53
+ puts "Database have been backed up on #{pastel.bold(@target_redmine.database.backup)}"
54
+ end
55
+
56
+ puts
57
+ puts "(Log is located on #{pastel.bold(logger.path)})"
66
58
  end
67
59
 
68
60
  end
@@ -1,248 +1,61 @@
1
- require 'fileutils'
2
- require 'notifier'
3
- require 'ansi'
4
- require 'io/console'
5
-
6
- module Redmine::Installer
1
+ module RedmineInstaller
7
2
  module Utils
8
3
 
9
- def self.included(base)
10
- base.send :extend, Methods
11
- base.send :include, Methods
4
+ PROGRESSBAR_FORMAT = ':elapsed [:bar] :percent'
12
5
 
13
- base.class_eval do
14
- self.send(:const_set, 'Git', Redmine::Installer::Git)
15
- end
6
+ def class_name
7
+ self.class.name.split('::').last
16
8
  end
17
9
 
18
- module Methods
19
-
20
- # =======================================================================
21
- # Generals
22
-
23
- def plugin
24
- Redmine::Installer::Plugin
25
- end
26
-
27
- def command
28
- Redmine::Installer::Command.instance
29
- end
30
-
31
- def error(*args)
32
- # Translate message
33
- if args.first.is_a?(Symbol)
34
- message = translate(*args)
35
- else
36
- message = args.first
37
- end
38
-
39
- # Colorize message
40
- colorize(message)
41
-
42
- raise Redmine::Installer::Error, message
43
- end
44
-
45
- # Try create a dir
46
- # When mkdir raise an error (permission problem) method
47
- # ask user if wants exist or try again
48
- def try_create_dir(dir)
49
- begin
50
- FileUtils.mkdir_p(dir)
51
- rescue
52
- choices = {}
53
- choices[:exit] = t(:exit)
54
- choices[:try_again] = t(:try_again)
55
-
56
- answer = choose(t(:dir_not_exist_and_cannot_be_created, dir: dir), choices, default: :exit)
57
-
58
- case answer
59
- when :exit
60
- error ''
61
- when :try_again
62
- try_create_dir(dir)
63
- end
64
- end
65
- end
66
-
67
- # Check if there are plugins in plugin dir
68
- def some_plugins?
69
- Dir.glob(File.join('plugins', '*')).select{|record| File.directory?(record)}.any?
70
- end
71
-
72
- def windows?
73
- RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
74
- end
75
-
76
- def root?
77
- Process.euid == 0
78
- end
79
-
80
-
81
- # =======================================================================
82
- # Input, output
83
-
84
- # Print message to stdout
85
- def say(message, lines=0)
86
- # Translate message
87
- if message.is_a?(Symbol)
88
- message = translate(message)
89
- end
90
-
91
- # Colorize message
92
- colorize(message)
93
-
94
- $stdout.print(message)
95
- lines.times { $stdout.puts }
96
- $stdout.flush
97
- end
98
-
99
- # Colorize text based on XML marks
100
- #
101
- # == Examples:
102
- # colorize("<bright><on_black><white>text</white></on_black></bright>")
103
- # # => "\e[1m\e[40m\e[37mtext\e[0m\e[0m\e[0m"
104
- #
105
- def colorize(text)
106
- return unless text.is_a?(String)
107
-
108
- text.gsub!(/<([a-zA-Z0-9_]+)>/) do
109
- if ANSI::CHART.has_key?($1.to_sym)
110
- ANSI.send($1)
111
- else
112
- "<#{$1}>"
113
- end
114
- end
115
- text.gsub!(/<\/([a-zA-Z0-9_]+)>/) do
116
- if ANSI::CHART.has_key?($1.to_sym)
117
- ANSI.clear
118
- else
119
- "</#{$1}>"
120
- end
121
- end
122
- end
123
-
124
- # Instead of `super` take only what I need
125
- def gets(hide=false)
126
- if hide
127
- input = $stdin.noecho{|io| io.gets}.to_s.chomp
128
- $stdout.puts # noecho is also for enter
129
- input
130
- else
131
- $stdin.gets.to_s.chomp
132
- end
133
- end
134
-
135
- # Asking on 1 line
136
- def ask(message=nil, options={})
137
- # Translate message
138
- if message.is_a?(Symbol)
139
- message = translate(message)
140
- end
141
- default = options[:default]
142
-
143
- if default
144
- message << " [#{default}]"
145
- end
146
-
147
- if !options[:without_colon]
148
- message << ': '
149
- end
150
-
151
- say(message)
152
- input = gets(options[:hide])
153
-
154
- # Ctrl-D or enter was pressed
155
- return default if input.empty?
156
-
157
- input
158
- end
159
-
160
- # User can choose from selection
161
- def choose(message, choices, options={})
162
- choices = choices.to_a
163
- default = options[:default]
164
- index = 1
165
-
166
- say(message, 1) unless message.nil?
167
- choices.each do |(key, message)|
168
- if key == default
169
- pre = '*'
170
- else
171
- pre = ' '
172
- end
173
-
174
- say(" #{pre}#{index}) #{message}", 1)
175
- index += 1
176
- end
177
-
178
- input = ask('> ', without_colon: true).to_i
179
- puts
180
-
181
- # Without default is input 0
182
- return default if input.zero? || input > choices.size
183
-
184
- choices[input-1][0]
185
- end
186
-
187
- # Say 'Do you want?'
188
- # -> YES, NO
189
- #
190
- def confirm(message, default=true)
191
- # Translate message
192
- if message.is_a?(Symbol)
193
- message = translate(message)
194
- end
195
-
196
- # Colorize message
197
- colorize(message)
198
-
199
- yes = t(:yes_t)
200
- no = t(:no_t)
201
-
202
- if default
203
- yes.upcase!
204
- else
205
- no.upcase!
206
- end
207
-
208
- message << " (#{yes}/#{no}): "
10
+ def logger
11
+ RedmineInstaller.logger
12
+ end
209
13
 
210
- $stdout.print(message)
211
- answer = gets
14
+ def prompt
15
+ RedmineInstaller.prompt
16
+ end
212
17
 
213
- return default if answer.empty?
18
+ def pastel
19
+ RedmineInstaller.pastel
20
+ end
214
21
 
215
- if answer[0].downcase == yes[0].downcase
216
- return true
217
- else
218
- return false
219
- end
220
- end
22
+ def run_command(cmd, title=nil)
23
+ RedmineInstaller::Command.new(cmd, title: title).run
24
+ end
221
25
 
26
+ def ok(title)
27
+ print "#{title} ... "
28
+ yield
29
+ puts pastel.green('OK')
30
+ rescue => e
31
+ puts pastel.red('FAIL')
32
+ raise
33
+ end
222
34
 
223
- # =======================================================================
224
- # Localizations
35
+ def env_user
36
+ ENV['USER'] || ENV['USERNAME']
37
+ end
225
38
 
226
- def translate(*args)
227
- I18n.translate(*args)
39
+ # Try create a dir
40
+ # When mkdir raise an error (permission problem) method ask user if wants exist or try again
41
+ def create_dir(dir)
42
+ FileUtils.mkdir_p(dir)
43
+ rescue
44
+ if prompt.yes?("Dir #{dir} doesn't exist and can't be created. Try again?", default: true)
45
+ create_dir(dir)
46
+ else
47
+ error('Dir creating was aborted by user.')
228
48
  end
229
- alias_method :t, :translate
230
-
231
-
232
- # =======================================================================
233
- # Notifications
49
+ end
234
50
 
235
- def notif(title, message=nil, image=nil)
236
- return unless Redmine::Installer.config.notif
51
+ def print_title(title)
52
+ puts
53
+ puts pastel.white.on_black(title)
54
+ end
237
55
 
238
- thread = ::Notifier.notify(
239
- title: title.to_s,
240
- message: message.to_s,
241
- image: image.to_s
242
- )
243
- thread.join
244
- end
56
+ def error(message)
57
+ raise RedmineInstaller::Error, message
58
+ end
245
59
 
246
- end # Methods
247
- end # Utils
248
- end # Redmine::Installer
60
+ end
61
+ end