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.
- checksums.yaml +7 -0
- data/.gitignore +25 -0
- data/Gemfile +19 -0
- data/LICENSE.txt +22 -0
- data/README.md +273 -0
- data/Rakefile +7 -0
- data/bin/redmine +8 -0
- data/lib/redmine-installer.rb +72 -0
- data/lib/redmine-installer/backup.rb +48 -0
- data/lib/redmine-installer/cli.rb +90 -0
- data/lib/redmine-installer/command.rb +142 -0
- data/lib/redmine-installer/config_param.rb +90 -0
- data/lib/redmine-installer/error.rb +5 -0
- data/lib/redmine-installer/exec.rb +158 -0
- data/lib/redmine-installer/ext/module.rb +7 -0
- data/lib/redmine-installer/ext/string.rb +15 -0
- data/lib/redmine-installer/git.rb +51 -0
- data/lib/redmine-installer/helper.rb +5 -0
- data/lib/redmine-installer/helpers/generate_config.rb +29 -0
- data/lib/redmine-installer/install.rb +56 -0
- data/lib/redmine-installer/locales/cs.yml +145 -0
- data/lib/redmine-installer/locales/en.yml +146 -0
- data/lib/redmine-installer/plugin.rb +9 -0
- data/lib/redmine-installer/plugins/base.rb +28 -0
- data/lib/redmine-installer/plugins/database.rb +168 -0
- data/lib/redmine-installer/plugins/email_sending.rb +82 -0
- data/lib/redmine-installer/plugins/redmine_plugin.rb +30 -0
- data/lib/redmine-installer/plugins/web_server.rb +26 -0
- data/lib/redmine-installer/profile.rb +74 -0
- data/lib/redmine-installer/step.rb +15 -0
- data/lib/redmine-installer/steps/backup.rb +120 -0
- data/lib/redmine-installer/steps/base.rb +60 -0
- data/lib/redmine-installer/steps/database_config.rb +15 -0
- data/lib/redmine-installer/steps/email_config.rb +11 -0
- data/lib/redmine-installer/steps/install.rb +21 -0
- data/lib/redmine-installer/steps/load_package.rb +180 -0
- data/lib/redmine-installer/steps/move_redmine.rb +22 -0
- data/lib/redmine-installer/steps/redmine_root.rb +29 -0
- data/lib/redmine-installer/steps/upgrade.rb +55 -0
- data/lib/redmine-installer/steps/validation.rb +38 -0
- data/lib/redmine-installer/steps/webserver_config.rb +22 -0
- data/lib/redmine-installer/task.rb +85 -0
- data/lib/redmine-installer/upgrade.rb +68 -0
- data/lib/redmine-installer/utils.rb +233 -0
- data/lib/redmine-installer/version.rb +5 -0
- data/redmine-installer.gemspec +25 -0
- data/spec/lib/install_spec.rb +46 -0
- data/spec/lib/upgrade_spec.rb +62 -0
- data/spec/load_redmine.rb +24 -0
- data/spec/spec_helper.rb +30 -0
- metadata +130 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
module Redmine::Installer::Step
|
2
|
+
class Base
|
3
|
+
|
4
|
+
include Redmine::Installer::Utils
|
5
|
+
|
6
|
+
attr_accessor :index
|
7
|
+
attr_accessor :base
|
8
|
+
attr_accessor :ran
|
9
|
+
|
10
|
+
def initialize(index, base)
|
11
|
+
self.index = index
|
12
|
+
self.base = base
|
13
|
+
self.ran = false
|
14
|
+
end
|
15
|
+
|
16
|
+
def print_title
|
17
|
+
title = '<bright><on_black><white>'
|
18
|
+
title << "#{index}. "
|
19
|
+
title << translate("step.#{self.class.class_name.underscore}.title"
|
20
|
+
)
|
21
|
+
title <<'</white></on_black></bright>'
|
22
|
+
|
23
|
+
say(title, 1)
|
24
|
+
end
|
25
|
+
|
26
|
+
def print_header
|
27
|
+
end
|
28
|
+
|
29
|
+
def print_footer
|
30
|
+
end
|
31
|
+
|
32
|
+
def final_step
|
33
|
+
end
|
34
|
+
|
35
|
+
def up
|
36
|
+
end
|
37
|
+
|
38
|
+
def down
|
39
|
+
end
|
40
|
+
|
41
|
+
def save(*)
|
42
|
+
end
|
43
|
+
|
44
|
+
def load(*)
|
45
|
+
end
|
46
|
+
|
47
|
+
def redmine_plugins
|
48
|
+
@redmine_plugins ||= _redmine_plugins
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def _redmine_plugins
|
54
|
+
Dir.glob(File.join(base.redmine_root, 'plugins', '*')).select do |entry|
|
55
|
+
File.directory?(entry)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Redmine::Installer::Step
|
2
|
+
class DatabaseConfig < Base
|
3
|
+
|
4
|
+
include Redmine::Installer::Helper::GenerateConfig
|
5
|
+
|
6
|
+
def up
|
7
|
+
if create_for(plugin::Database)
|
8
|
+
# continue
|
9
|
+
else
|
10
|
+
base.settings[:skip_migration] = true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Redmine::Installer::Step
|
2
|
+
class Install < Base
|
3
|
+
|
4
|
+
def up
|
5
|
+
Dir.chdir(base.tmp_redmine_root) do
|
6
|
+
command.bundle_install(base.env)
|
7
|
+
|
8
|
+
return if base.settings[:skip_migration]
|
9
|
+
|
10
|
+
command.rake_db_create(base.env)
|
11
|
+
command.rake_db_migrate(base.env)
|
12
|
+
command.rake_redmine_plugin_migrate(base.env) if some_plugins?
|
13
|
+
command.rake_generate_secret_token(base.env)
|
14
|
+
|
15
|
+
# Other plugins can have post-install procedure
|
16
|
+
plugin::RedminePlugin.all.each(&:install)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'rubygems/package'
|
2
|
+
require 'ruby-progressbar'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'zip'
|
5
|
+
require 'tmpdir'
|
6
|
+
require 'zlib'
|
7
|
+
|
8
|
+
module Redmine::Installer::Step
|
9
|
+
class LoadPackage < Base
|
10
|
+
|
11
|
+
SUPPORTED_ARCHIVE_FORMATS = ['.zip', '.gz', '.tgz']
|
12
|
+
TAR_LONGLINK = '././@LongLink'
|
13
|
+
PROGRESSBAR_FORMAT = '%a [%b>%i] %p%% %t'
|
14
|
+
|
15
|
+
def up
|
16
|
+
case base.options[:source]
|
17
|
+
when 'file'
|
18
|
+
load_file
|
19
|
+
when 'git'
|
20
|
+
load_git
|
21
|
+
else
|
22
|
+
error :error_unsupported_source, source: base.options[:source]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def final_step
|
27
|
+
# Delete tmp_redmine_root
|
28
|
+
FileUtils.remove_entry_secure(@tmpdir) if @tmpdir
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# =======================================================================
|
34
|
+
# General
|
35
|
+
|
36
|
+
def create_tmp_dir
|
37
|
+
@tmpdir = Dir.mktmpdir
|
38
|
+
rescue
|
39
|
+
FileUtils.remove_entry_secure(@tmpdir)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Move files from temp dir to target. First check
|
43
|
+
# if folder contains redmine or contains
|
44
|
+
# folder which contains redmine :-)
|
45
|
+
#
|
46
|
+
# Package can have format:
|
47
|
+
# |-- redmine-2
|
48
|
+
# |-- app
|
49
|
+
# `-- config
|
50
|
+
# ...
|
51
|
+
#
|
52
|
+
def get_tmp_redmine_root
|
53
|
+
base.tmp_redmine_root = @tmpdir
|
54
|
+
|
55
|
+
loop {
|
56
|
+
ls = Dir.glob(File.join(base.tmp_redmine_root, '*'))
|
57
|
+
|
58
|
+
if ls.size == 1
|
59
|
+
base.tmp_redmine_root = ls.first
|
60
|
+
else
|
61
|
+
break
|
62
|
+
end
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
# =======================================================================
|
68
|
+
# File
|
69
|
+
|
70
|
+
def load_file
|
71
|
+
unless File.exist?(base.package)
|
72
|
+
error :file_not_exist, file: base.package
|
73
|
+
end
|
74
|
+
|
75
|
+
@type = File.extname(base.package)
|
76
|
+
unless SUPPORTED_ARCHIVE_FORMATS.include?(@type)
|
77
|
+
error :file_must_have_format, file: base.package, formats: SUPPORTED_ARCHIVE_FORMATS.join(', ')
|
78
|
+
end
|
79
|
+
|
80
|
+
# Make temp directory and extract archive + move it to the redmine_folder
|
81
|
+
extract_to_tmp
|
82
|
+
|
83
|
+
# Locate redmine_root in tmpdir
|
84
|
+
get_tmp_redmine_root
|
85
|
+
end
|
86
|
+
|
87
|
+
def extract_to_tmp
|
88
|
+
create_tmp_dir
|
89
|
+
|
90
|
+
case @type
|
91
|
+
when '.zip'
|
92
|
+
extract_zip
|
93
|
+
when '.gz', '.tgz'
|
94
|
+
extract_tar_gz
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def extract_zip
|
99
|
+
Zip::File.open(base.package) do |zip_file|
|
100
|
+
# Progressbar
|
101
|
+
progressbar = ProgressBar.create(format: PROGRESSBAR_FORMAT, total: zip_file.size)
|
102
|
+
|
103
|
+
zip_file.each do |entry|
|
104
|
+
dest_file = File.join(@tmpdir, entry.name)
|
105
|
+
FileUtils.mkdir_p(File.dirname(dest_file))
|
106
|
+
|
107
|
+
entry.extract(dest_file)
|
108
|
+
progressbar.increment
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Extract .tar.gz archive
|
114
|
+
# based on http://dracoater.blogspot.cz/2013/10/extracting-files-from-targz-with-ruby.html
|
115
|
+
#
|
116
|
+
# Originally tar did not support paths longer than 100 chars. GNU tar is better and they
|
117
|
+
# implemented support for longer paths, but it was made through a hack called ././@LongLink.
|
118
|
+
# Shortly speaking, if you stumble upon an entry in tar archive which path equals to above
|
119
|
+
# mentioned ././@LongLink, that means that the following entry path is longer than 100 chars and
|
120
|
+
# is truncated. The full path of the following entry is actually the value of the current entry.
|
121
|
+
#
|
122
|
+
def extract_tar_gz
|
123
|
+
Gem::Package::TarReader.new(Zlib::GzipReader.open(base.package)) do |tar|
|
124
|
+
|
125
|
+
# Progressbar
|
126
|
+
progressbar = ProgressBar.create(format: PROGRESSBAR_FORMAT, total: tar.count)
|
127
|
+
|
128
|
+
# tar.count move position pointer to end
|
129
|
+
tar.rewind
|
130
|
+
|
131
|
+
dest_file = nil
|
132
|
+
tar.each do |entry|
|
133
|
+
if entry.full_name == TAR_LONGLINK
|
134
|
+
dest_file = File.join(@tmpdir, entry.read.strip)
|
135
|
+
next
|
136
|
+
end
|
137
|
+
dest_file ||= File.join(@tmpdir, entry.full_name)
|
138
|
+
if entry.directory?
|
139
|
+
FileUtils.rm_rf(dest_file) unless File.directory?(dest_file)
|
140
|
+
FileUtils.mkdir_p(dest_file, mode: entry.header.mode, verbose: false)
|
141
|
+
elsif entry.file?
|
142
|
+
FileUtils.rm_rf(dest_file) unless File.file?(dest_file)
|
143
|
+
File.open(dest_file, 'wb') do |f|
|
144
|
+
f.write(entry.read)
|
145
|
+
end
|
146
|
+
FileUtils.chmod(entry.header.mode, dest_file, verbose: false)
|
147
|
+
elsif entry.header.typeflag == '2' # symlink
|
148
|
+
File.symlink(entry.header.linkname, dest_file)
|
149
|
+
end
|
150
|
+
|
151
|
+
dest_file = nil
|
152
|
+
progressbar.increment
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
# =======================================================================
|
159
|
+
# Git
|
160
|
+
|
161
|
+
def load_git
|
162
|
+
create_tmp_dir
|
163
|
+
|
164
|
+
# Package is remote url to git repository
|
165
|
+
remote = base.package
|
166
|
+
|
167
|
+
if remote
|
168
|
+
# Clone repository
|
169
|
+
Git.clone(remote, @tmpdir, base.options['branch'])
|
170
|
+
else
|
171
|
+
# Copy current repository to tmp and run pull
|
172
|
+
Git.copy_and_fetch(base.redmine_root, @tmpdir)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Locate redmine_root in tmpdir
|
176
|
+
get_tmp_redmine_root
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Redmine::Installer::Step
|
4
|
+
class MoveRedmine < Base
|
5
|
+
|
6
|
+
def up
|
7
|
+
# Move all files from tmp_redmine_root to redmine_root
|
8
|
+
Dir.chdir(base.tmp_redmine_root) do
|
9
|
+
Dir.glob('{*,.*}') do |entry|
|
10
|
+
next if entry.end_with?('.') || entry.end_with?('..')
|
11
|
+
|
12
|
+
FileUtils.mv(entry, base.redmine_root)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def print_footer
|
18
|
+
say '<green>... OK</green>', 1
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Redmine::Installer::Step
|
2
|
+
class RedmineRoot < Base
|
3
|
+
|
4
|
+
def up
|
5
|
+
# Get redmine root
|
6
|
+
base.redmine_root ||= ask(:path_for_redmine_root, default: '.')
|
7
|
+
|
8
|
+
# Make absolute path
|
9
|
+
base.redmine_root = File.expand_path(base.redmine_root)
|
10
|
+
|
11
|
+
unless Dir.exist?(base.redmine_root)
|
12
|
+
try_create_dir(base.redmine_root)
|
13
|
+
end
|
14
|
+
|
15
|
+
unless File.writable?(base.redmine_root)
|
16
|
+
error t(:dir_is_not_writeable, dir: base.redmine_root)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def save(configuration)
|
21
|
+
configuration['redmine_root'] = base.redmine_root
|
22
|
+
end
|
23
|
+
|
24
|
+
def load(configuration)
|
25
|
+
base.redmine_root = configuration['redmine_root']
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Redmine::Installer::Step
|
4
|
+
class Upgrade < Base
|
5
|
+
|
6
|
+
def up
|
7
|
+
# Copy database.yml
|
8
|
+
copy_config_file(plugin::Database::DATABASE_YML_PATH)
|
9
|
+
|
10
|
+
# Copy configuration.yml
|
11
|
+
copy_config_file(plugin::EmailSending::CONFIGURATION_YML_PATH)
|
12
|
+
|
13
|
+
# Copy files
|
14
|
+
FileUtils.cp_r(File.join(base.redmine_root, 'files'), base.tmp_redmine_root)
|
15
|
+
|
16
|
+
# Copy plugins
|
17
|
+
tmp_plugin_dir = File.join(base.tmp_redmine_root, 'plugins')
|
18
|
+
redmine_plugins.each do |plugin|
|
19
|
+
# Copy only plugins which are not part of package
|
20
|
+
# - this is a upgrade so package should contains newer version
|
21
|
+
unless Dir.exist?(File.join(tmp_plugin_dir, File.basename(plugin)))
|
22
|
+
FileUtils.cp_r(plugin, tmp_plugin_dir)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
Dir.chdir(base.tmp_redmine_root) do
|
27
|
+
command.bundle_install(base.env)
|
28
|
+
command.rake_db_migrate(base.env)
|
29
|
+
command.rake_redmine_plugin_migrate(base.env) if some_plugins?
|
30
|
+
command.rake_generate_secret_token(base.env)
|
31
|
+
|
32
|
+
# Other plugins can have post-install procedure
|
33
|
+
plugin::RedminePlugin.all.each(&:upgrade)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Delete content of redmine_root
|
37
|
+
Dir.glob(File.join(base.redmine_root, '{*,.*}')) do |entry|
|
38
|
+
next if entry.end_with?('.') || entry.end_with?('..')
|
39
|
+
|
40
|
+
FileUtils.remove_entry_secure(entry)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def copy_config_file(relative_path)
|
47
|
+
file = File.join(base.redmine_root, relative_path)
|
48
|
+
|
49
|
+
if File.exist?(file)
|
50
|
+
FileUtils.cp(file, File.join(base.tmp_redmine_root, 'config'))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
##
|
2
|
+
# Validate redmine folder
|
3
|
+
#
|
4
|
+
module Redmine::Installer::Step
|
5
|
+
class Validation < Base
|
6
|
+
|
7
|
+
REDMINE_SHOULD_CONTAINS = [
|
8
|
+
'app', 'lib', 'config', 'public', 'db',
|
9
|
+
'Gemfile', 'Rakefile', 'config.ru',
|
10
|
+
File.join('lib', 'redmine'),
|
11
|
+
File.join('lib', 'redmine', 'core_ext'),
|
12
|
+
File.join('lib', 'redmine', 'helpers'),
|
13
|
+
File.join('lib', 'redmine', 'views'),
|
14
|
+
File.join('lib', 'redmine.rb'),
|
15
|
+
].sort
|
16
|
+
|
17
|
+
def up
|
18
|
+
Dir.chdir(base.redmine_root) do
|
19
|
+
@records = Dir.glob(File.join('**', '*')).sort
|
20
|
+
end
|
21
|
+
|
22
|
+
# Is this redmine
|
23
|
+
unless (@records & REDMINE_SHOULD_CONTAINS) == REDMINE_SHOULD_CONTAINS
|
24
|
+
error :error_redmine_not_contains_all, records: REDMINE_SHOULD_CONTAINS.join(', ')
|
25
|
+
end
|
26
|
+
|
27
|
+
# Plugins are in righ dir
|
28
|
+
if @records.select{|x| x.start_with?('vendor/plugins')}.size > 1
|
29
|
+
error :error_plugins_should_be_on_plugins
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def print_footer
|
34
|
+
say '<green>... OK</green>', 1
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Redmine::Installer::Step
|
2
|
+
class WebserverConfig < Base
|
3
|
+
|
4
|
+
def up
|
5
|
+
choices = {}
|
6
|
+
plugin::WebServer.all.each do |m|
|
7
|
+
choices[m] = m.title
|
8
|
+
end
|
9
|
+
choices[nil] = t(:skip)
|
10
|
+
|
11
|
+
answer = choose(:"what_web_server_do_you_want", choices, default: nil)
|
12
|
+
|
13
|
+
# Skip
|
14
|
+
return if answer.nil?
|
15
|
+
|
16
|
+
say("(#{answer.title })", 5)
|
17
|
+
|
18
|
+
say(answer.generate_config(base.redmine_root))
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|