wordmove 5.2.2 → 6.0.0.alpha.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +12 -5
- data/.rubocop.yml +2 -5
- data/.ruby-version +1 -1
- data/.yardopts +1 -0
- data/CONTRIBUTING.md +15 -0
- data/Rakefile +4 -3
- data/exe/wordmove +2 -1
- data/lib/wordmove/actions/adapt_local_db.rb +101 -0
- data/lib/wordmove/actions/adapt_remote_db.rb +88 -0
- data/lib/wordmove/actions/backup_local_db.rb +56 -0
- data/lib/wordmove/actions/delete_local_file.rb +34 -0
- data/lib/wordmove/actions/delete_remote_file.rb +43 -0
- data/lib/wordmove/actions/filter_and_setup_tasks_to_run.rb +42 -0
- data/lib/wordmove/actions/ftp/backup_remote_db.rb +54 -0
- data/lib/wordmove/actions/ftp/cleanup_after_adapt.rb +70 -0
- data/lib/wordmove/actions/ftp/download_remote_db.rb +69 -0
- data/lib/wordmove/actions/ftp/get_directory.rb +67 -0
- data/lib/wordmove/actions/ftp/helpers.rb +91 -0
- data/lib/wordmove/actions/ftp/pull_wordpress.rb +56 -0
- data/lib/wordmove/actions/ftp/push_wordpress.rb +54 -0
- data/lib/wordmove/actions/ftp/put_and_import_dump_remotely.rb +81 -0
- data/lib/wordmove/actions/ftp/put_directory.rb +67 -0
- data/lib/wordmove/actions/get_file.rb +38 -0
- data/lib/wordmove/actions/helpers.rb +142 -0
- data/lib/wordmove/actions/put_file.rb +48 -0
- data/lib/wordmove/actions/run_after_pull_hook.rb +26 -0
- data/lib/wordmove/actions/run_after_push_hook.rb +26 -0
- data/lib/wordmove/actions/run_before_pull_hook.rb +26 -0
- data/lib/wordmove/actions/run_before_push_hook.rb +26 -0
- data/lib/wordmove/actions/run_local_command.rb +34 -0
- data/lib/wordmove/actions/setup_context_for_db.rb +73 -0
- data/lib/wordmove/actions/ssh/backup_remote_db.rb +49 -0
- data/lib/wordmove/actions/ssh/cleanup_after_adapt.rb +54 -0
- data/lib/wordmove/actions/ssh/download_remote_db.rb +81 -0
- data/lib/wordmove/actions/ssh/get_directory.rb +76 -0
- data/lib/wordmove/actions/ssh/helpers.rb +128 -0
- data/lib/wordmove/actions/ssh/pull_wordpress.rb +56 -0
- data/lib/wordmove/actions/ssh/push_wordpress.rb +54 -0
- data/lib/wordmove/actions/ssh/put_and_import_dump_remotely.rb +75 -0
- data/lib/wordmove/actions/ssh/put_directory.rb +76 -0
- data/lib/wordmove/actions/ssh/run_remote_command.rb +39 -0
- data/lib/wordmove/assets/dump.php.erb +6 -6
- data/lib/wordmove/assets/import.php.erb +7 -7
- data/lib/wordmove/assets/wordmove_schema_global.yml +2 -0
- data/lib/wordmove/cli.rb +152 -91
- data/lib/wordmove/db_paths_config.rb +44 -0
- data/lib/wordmove/doctor/movefile.rb +8 -8
- data/lib/wordmove/doctor/mysql.rb +18 -15
- data/lib/wordmove/doctor/rsync.rb +2 -2
- data/lib/wordmove/doctor/ssh.rb +3 -3
- data/lib/wordmove/doctor/wpcli.rb +5 -5
- data/lib/wordmove/environments_list.rb +4 -4
- data/lib/wordmove/exceptions.rb +13 -0
- data/lib/wordmove/generators/movefile.rb +7 -5
- data/lib/wordmove/generators/movefile_adapter.rb +11 -5
- data/lib/wordmove/guardian.rb +5 -5
- data/lib/wordmove/hook.rb +13 -14
- data/lib/wordmove/logger.rb +11 -10
- data/lib/wordmove/movefile.rb +63 -59
- data/lib/wordmove/organizers/ftp/pull.rb +53 -0
- data/lib/wordmove/organizers/ftp/push.rb +54 -0
- data/lib/wordmove/organizers/ssh/pull.rb +53 -0
- data/lib/wordmove/organizers/ssh/push.rb +54 -0
- data/lib/wordmove/version.rb +1 -1
- data/lib/wordmove/wordpress_directory.rb +76 -8
- data/lib/wordmove/wpcli.rb +88 -0
- data/lib/wordmove.rb +33 -11
- data/wordmove.gemspec +37 -30
- metadata +139 -35
- data/lib/wordmove/deployer/base.rb +0 -193
- data/lib/wordmove/deployer/ftp.rb +0 -160
- data/lib/wordmove/deployer/ssh/default_sql_adapter.rb +0 -47
- data/lib/wordmove/deployer/ssh/wpcli_sql_adapter.rb +0 -55
- data/lib/wordmove/deployer/ssh.rb +0 -169
- data/lib/wordmove/sql_adapter/default.rb +0 -68
- data/lib/wordmove/sql_adapter/wpcli.rb +0 -54
- data/lib/wordmove/wordpress_directory/path.rb +0 -11
@@ -0,0 +1,67 @@
|
|
1
|
+
module Wordmove
|
2
|
+
module Actions
|
3
|
+
module Ftp
|
4
|
+
# Syncs a whole directory over FTP protocol from the remote server to local host
|
5
|
+
class GetDirectory
|
6
|
+
extend LightService::Action
|
7
|
+
include Wordmove::Actions::Helpers
|
8
|
+
include WordpressDirectory::RemoteHelperMethods
|
9
|
+
include WordpressDirectory::LocalHelperMethods
|
10
|
+
|
11
|
+
# :folder_task is expected to be one symbol from Wordmove::CLI.wordpress_options array
|
12
|
+
expects :logger,
|
13
|
+
:local_options,
|
14
|
+
:remote_options,
|
15
|
+
:cli_options,
|
16
|
+
:photocopier,
|
17
|
+
:folder_task
|
18
|
+
|
19
|
+
# @!method execute
|
20
|
+
# @param logger [Wordmove::Logger]
|
21
|
+
# @param local_options [Hash] Local host options fetched from
|
22
|
+
# movefile (with symbolized keys)
|
23
|
+
# @param remote_options [Hash] Remote host options fetched from
|
24
|
+
# movefile (with symbolized keys)
|
25
|
+
# @param cli_options [Hash] Command line options (with symbolized keys)
|
26
|
+
# @param photocopier [Photocopier::FTP]
|
27
|
+
# @param folder_task [Symbol] Symbolazied folder name
|
28
|
+
# @!scope class
|
29
|
+
# @return [LightService::Context] Action's context
|
30
|
+
executed do |context|
|
31
|
+
context.logger.task "Pulling #{context.folder_task}"
|
32
|
+
|
33
|
+
next context if simulate?(cli_options: context.cli_options)
|
34
|
+
|
35
|
+
command = 'get_directory'
|
36
|
+
|
37
|
+
# This action can generate `command_args` by itself,
|
38
|
+
# but it gives the context the chance to ovveride it.
|
39
|
+
# By the way this variable is not `expects`ed.
|
40
|
+
# Note that we do not use the second argument to `fetch`
|
41
|
+
# to express a default value, because it would be greedly interpreted
|
42
|
+
# but if `command_args` is already defined by context, then it's
|
43
|
+
# possible that `"remote_#{context.folder_task}_dir"` could
|
44
|
+
# not be defined.
|
45
|
+
command_args = context.fetch(:command_args) || [
|
46
|
+
send(
|
47
|
+
"remote_#{context.folder_task}_dir",
|
48
|
+
remote_options: context.remote_options
|
49
|
+
).path,
|
50
|
+
send(
|
51
|
+
"local_#{context.folder_task}_dir",
|
52
|
+
local_options: context.local_options
|
53
|
+
).path,
|
54
|
+
paths_to_exclude(remote_options: context.remote_options)
|
55
|
+
]
|
56
|
+
|
57
|
+
context.logger.task_step false, "#{command}: #{command_args.join(' ')}"
|
58
|
+
result = context.photocopier.send(command, *command_args)
|
59
|
+
|
60
|
+
next context if result == true
|
61
|
+
|
62
|
+
context.fail!("Failed to pull #{context.folder_task}")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Wordmove
|
2
|
+
module Actions
|
3
|
+
module Ftp
|
4
|
+
module Helpers
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
class_methods do # rubocop:disable Metrics/BlockLength
|
8
|
+
#
|
9
|
+
# (In)Utility method to retrieve ftp options from the superset of remote options
|
10
|
+
#
|
11
|
+
# @param [Hash] remote_options Remote host options fetched from movefile
|
12
|
+
# (with symbolized keys)
|
13
|
+
#
|
14
|
+
# @return [Hash] Ftp options from the movefile
|
15
|
+
#
|
16
|
+
def ftp_options(remote_options:)
|
17
|
+
remote_options[:ftp]
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Escape a string to be printed into PHP files
|
22
|
+
#
|
23
|
+
# @param [String] string The string to escape
|
24
|
+
#
|
25
|
+
# @return [String] The escaped string
|
26
|
+
#
|
27
|
+
def escape_php(string:)
|
28
|
+
return '' unless string
|
29
|
+
|
30
|
+
# replaces \ with \\
|
31
|
+
# replaces ' with \'
|
32
|
+
string.gsub('\\', '\\\\\\').gsub(/'/, '\\\\\'')
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Generate a token
|
37
|
+
#
|
38
|
+
# @return [String] A random hexadecimal string
|
39
|
+
#
|
40
|
+
def remote_php_scripts_token
|
41
|
+
SecureRandom.hex(40)
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# Generate THE PHP dump script, protected by a token to ensure only Wordmove will run it
|
46
|
+
#
|
47
|
+
# @param [Hash] remote_db_options The remote DB configurations fetched from movefile
|
48
|
+
# @param [String] token The token that will be used to protect the execution of the script
|
49
|
+
#
|
50
|
+
# @return [String] The PHP file as string
|
51
|
+
#
|
52
|
+
def generate_dump_script(remote_db_options:, token:)
|
53
|
+
template = ERB.new(
|
54
|
+
File.read(File.join(File.dirname(__FILE__), '../../assets/dump.php.erb'))
|
55
|
+
)
|
56
|
+
template.result(binding)
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Generate THE PHP import script, protected by a token to ensure only Wordmove will run it
|
61
|
+
#
|
62
|
+
# @param [Hash] remote_db_options The remote DB configurations fetched from movefile
|
63
|
+
# @param [String] token The token that will be used to protect the execution of the script
|
64
|
+
#
|
65
|
+
# @return [String] The PHP file as string
|
66
|
+
#
|
67
|
+
def generate_import_script(remote_db_options:, token:)
|
68
|
+
template = ERB.new(
|
69
|
+
File.read(File.join(File.dirname(__FILE__), '../../assets/import.php.erb'))
|
70
|
+
)
|
71
|
+
template.result(binding)
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Download a file from the internet making a simple GET request
|
76
|
+
#
|
77
|
+
# @param [String] url The URL of the resource to download
|
78
|
+
# @param [String] local_path The local path where the resource will be saved
|
79
|
+
#
|
80
|
+
# @return [nil]
|
81
|
+
#
|
82
|
+
def download(url:, local_path:)
|
83
|
+
File.open(local_path, 'w') do |file|
|
84
|
+
file << URI.parse(url).read
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Wordmove
|
2
|
+
module Actions
|
3
|
+
module Ftp
|
4
|
+
# Syncs wordpress folder (usually root folder), exluding +wp-content/+ folder, over FTP
|
5
|
+
# protocol from the remote server to local host
|
6
|
+
class PullWordpress
|
7
|
+
extend ::LightService::Action
|
8
|
+
include Wordmove::Actions::Helpers
|
9
|
+
include WordpressDirectory::RemoteHelperMethods
|
10
|
+
|
11
|
+
expects :remote_options,
|
12
|
+
:local_options,
|
13
|
+
:logger,
|
14
|
+
:photocopier
|
15
|
+
|
16
|
+
# @!method execute
|
17
|
+
# @param remote_options [Hash] Remote host options fetched from
|
18
|
+
# movefile (with symbolized keys)
|
19
|
+
# @param local_options [Hash] Local host options fetched from
|
20
|
+
# movefile (with symbolized keys)
|
21
|
+
# @param logger [Wordmove::Logger]
|
22
|
+
# @param photocopier [Photocopier::FTP]
|
23
|
+
# @!scope class
|
24
|
+
# @return [LightService::Context] Action's context
|
25
|
+
executed do |context|
|
26
|
+
local_path = context.local_options[:wordpress_path]
|
27
|
+
|
28
|
+
remote_path = context.remote_options[:wordpress_path]
|
29
|
+
|
30
|
+
wp_content_relative_path = remote_wp_content_dir(
|
31
|
+
remote_options: context.remote_options
|
32
|
+
).relative_path
|
33
|
+
|
34
|
+
exclude_wp_content = exclude_dir_contents(
|
35
|
+
path: wp_content_relative_path
|
36
|
+
)
|
37
|
+
|
38
|
+
exclude_paths = paths_to_exclude(
|
39
|
+
remote_options: context.remote_options
|
40
|
+
).push(exclude_wp_content)
|
41
|
+
|
42
|
+
result = Wordmove::Actions::Ftp::GetDirectory.execute(
|
43
|
+
photocopier: context.photocopier,
|
44
|
+
logger: context.logger,
|
45
|
+
command_args: [remote_path, local_path, exclude_paths],
|
46
|
+
folder_task: :wordpress,
|
47
|
+
local_options: context.local_options,
|
48
|
+
remote_options: context.remote_options,
|
49
|
+
cli_options: context.cli_options
|
50
|
+
)
|
51
|
+
context.fail!(result.message) if result.failure?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Wordmove
|
2
|
+
module Actions
|
3
|
+
module Ftp
|
4
|
+
# Syncs wordpress folder (usually root folder), exluding +wp-content/+ folder, over FTP
|
5
|
+
# protocol from local host to the remote server
|
6
|
+
class PushWordpress
|
7
|
+
extend ::LightService::Action
|
8
|
+
include Wordmove::Actions::Helpers
|
9
|
+
include WordpressDirectory::LocalHelperMethods
|
10
|
+
|
11
|
+
expects :remote_options,
|
12
|
+
:local_options,
|
13
|
+
:logger,
|
14
|
+
:photocopier
|
15
|
+
|
16
|
+
# @!method execute
|
17
|
+
# @param remote_options [Hash] Remote host options fetched from
|
18
|
+
# movefile (with symbolized keys)
|
19
|
+
# @param local_options [Hash] Local host options fetched from
|
20
|
+
# movefile (with symbolized keys)
|
21
|
+
# @param logger [Wordmove::Logger]
|
22
|
+
# @param photocopier [Photocopier::FTP]
|
23
|
+
# @!scope class
|
24
|
+
# @return [LightService::Context] Action's context
|
25
|
+
executed do |context|
|
26
|
+
local_path = context.local_options[:wordpress_path]
|
27
|
+
|
28
|
+
remote_path = context.remote_options[:wordpress_path]
|
29
|
+
|
30
|
+
wp_content_relative_path = local_wp_content_dir(
|
31
|
+
local_options: context.local_options
|
32
|
+
).relative_path
|
33
|
+
|
34
|
+
exclude_wp_content = exclude_dir_contents(path: wp_content_relative_path)
|
35
|
+
|
36
|
+
exclude_paths = paths_to_exclude(
|
37
|
+
remote_options: context.remote_options
|
38
|
+
).push(exclude_wp_content)
|
39
|
+
|
40
|
+
result = Wordmove::Actions::Ftp::PutDirectory.execute(
|
41
|
+
photocopier: context.photocopier,
|
42
|
+
logger: context.logger,
|
43
|
+
command_args: [local_path, remote_path, exclude_paths],
|
44
|
+
folder_task: :wordpress,
|
45
|
+
local_options: context.local_options,
|
46
|
+
remote_options: context.remote_options,
|
47
|
+
cli_options: context.cli_options
|
48
|
+
)
|
49
|
+
context.fail!(result.message) if result.failure?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Wordmove
|
2
|
+
module Actions
|
3
|
+
module Ftp
|
4
|
+
# Uploads a DB dump to remote host and import it in the remote database over FTP protocol
|
5
|
+
class PutAndImportDumpRemotely
|
6
|
+
extend ::LightService::Action
|
7
|
+
include Wordmove::Actions::Helpers
|
8
|
+
include Wordmove::Actions::Ftp::Helpers
|
9
|
+
include WordpressDirectory::RemoteHelperMethods
|
10
|
+
include WordpressDirectory::LocalHelperMethods
|
11
|
+
|
12
|
+
expects :remote_options,
|
13
|
+
:cli_options,
|
14
|
+
:logger,
|
15
|
+
:photocopier,
|
16
|
+
:db_paths
|
17
|
+
|
18
|
+
# @!method execute
|
19
|
+
# @param logger [Wordmove::Logger]
|
20
|
+
# @param cli_options [Hash] Command line options (with symbolized keys)
|
21
|
+
# @param remote_options [Hash] Remote host options fetched from
|
22
|
+
# movefile (with symbolized keys)
|
23
|
+
# @param db_paths [BbPathsConfig] Configuration object for database
|
24
|
+
# @param photocopier [Photocopier::FTP]
|
25
|
+
# @!scope class
|
26
|
+
# @return [LightService::Context] Action's context
|
27
|
+
executed do |context| # rubocop:disable Metrics/BlockLength
|
28
|
+
context.logger.task 'Upload and import adapted DB'
|
29
|
+
|
30
|
+
result = Wordmove::Actions::PutFile.execute(
|
31
|
+
logger: context.logger,
|
32
|
+
photocopier: context.photocopier,
|
33
|
+
cli_options: context.cli_options,
|
34
|
+
command_args: [
|
35
|
+
context.db_paths.local.adapted_path,
|
36
|
+
context.db_paths.remote.path
|
37
|
+
]
|
38
|
+
)
|
39
|
+
context.fail_and_return!(result.message) if result.failure?
|
40
|
+
|
41
|
+
result = Wordmove::Actions::PutFile.execute(
|
42
|
+
logger: context.logger,
|
43
|
+
photocopier: context.photocopier,
|
44
|
+
cli_options: context.cli_options,
|
45
|
+
command_args: [
|
46
|
+
context.db_paths.ftp.local.generated_import_script_path,
|
47
|
+
context.db_paths.ftp.remote.import_script_path
|
48
|
+
]
|
49
|
+
)
|
50
|
+
context.fail_and_return!(result.message) if result.failure?
|
51
|
+
|
52
|
+
import_url = [
|
53
|
+
context.db_paths.ftp.remote.import_script_url,
|
54
|
+
'?shared_key=',
|
55
|
+
context.db_paths.ftp.token,
|
56
|
+
'&start=1&foffset=0&totalqueries=0&fn=dump.sql'
|
57
|
+
].join
|
58
|
+
|
59
|
+
download(url: import_url, local_path: context.db_paths.ftp.local.temp_path)
|
60
|
+
|
61
|
+
if context.cli_options[:debug]
|
62
|
+
context.logger.debug "Operation log located at: #{context.db_paths.ftp.local.temp_path}"
|
63
|
+
else
|
64
|
+
result = Wordmove::Actions::DeleteLocalFile.execute(
|
65
|
+
cli_options: context.cli_options,
|
66
|
+
logger: context.logger,
|
67
|
+
file_path: context.db_paths.ftp.local.temp_path
|
68
|
+
)
|
69
|
+
|
70
|
+
if result.failure?
|
71
|
+
context.logger.warning 'Failed to delete local file ' \
|
72
|
+
"#{context.db_paths.ftp.local.temp_path} because: " \
|
73
|
+
"#{result.message}" \
|
74
|
+
'. Manual intervention required'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Wordmove
|
2
|
+
module Actions
|
3
|
+
module Ftp
|
4
|
+
# Syncs a whole directory over FTP protocol from local host to remote server
|
5
|
+
class PutDirectory
|
6
|
+
extend LightService::Action
|
7
|
+
include Wordmove::Actions::Helpers
|
8
|
+
include WordpressDirectory::LocalHelperMethods
|
9
|
+
include WordpressDirectory::RemoteHelperMethods
|
10
|
+
|
11
|
+
# :folder_task is expected to be one symbol from Wordmove::CLI.wordpress_options array
|
12
|
+
expects :logger,
|
13
|
+
:local_options,
|
14
|
+
:remote_options,
|
15
|
+
:cli_options,
|
16
|
+
:photocopier,
|
17
|
+
:folder_task
|
18
|
+
|
19
|
+
# @!method execute
|
20
|
+
# @param logger [Wordmove::Logger]
|
21
|
+
# @param local_options [Hash] Local host options fetched from
|
22
|
+
# movefile (with symbolized keys)
|
23
|
+
# @param remote_options [Hash] Remote host options fetched from
|
24
|
+
# movefile (with symbolized keys)
|
25
|
+
# @param cli_options [Hash] Command line options (with symbolized keys)
|
26
|
+
# @param photocopier [Photocopier::FTP]
|
27
|
+
# @param folder_task [Symbol] Symbolazied folder name
|
28
|
+
# @!scope class
|
29
|
+
# @return [LightService::Context] Action's context
|
30
|
+
executed do |context|
|
31
|
+
context.logger.task "Pushing #{context.folder_task}"
|
32
|
+
|
33
|
+
next context if simulate?(cli_options: context.cli_options)
|
34
|
+
|
35
|
+
command = 'put_directory'
|
36
|
+
|
37
|
+
# This action can generate `command_args` by itself,
|
38
|
+
# but it gives the context the chance to ovveride it.
|
39
|
+
# By the way this variable is not `expects`ed.
|
40
|
+
# Note that we do not use the second argument to `fetch`
|
41
|
+
# to express a default value, because it would be greedly interpreted
|
42
|
+
# but if `command_args` is already defined by context, then it's
|
43
|
+
# possible that `"local_#{context.folder_task}_dir"` could
|
44
|
+
# not be defined.
|
45
|
+
command_args = context.fetch(:command_args) || [
|
46
|
+
send(
|
47
|
+
"local_#{context.folder_task}_dir",
|
48
|
+
local_options: context.local_options
|
49
|
+
).path,
|
50
|
+
send(
|
51
|
+
"remote_#{context.folder_task}_dir",
|
52
|
+
remote_options: context.remote_options
|
53
|
+
).path,
|
54
|
+
paths_to_exclude(remote_options: context.remote_options)
|
55
|
+
]
|
56
|
+
|
57
|
+
context.logger.task_step false, "#{command}: #{command_args.join(' ')}"
|
58
|
+
result = context.photocopier.send(command, *command_args)
|
59
|
+
|
60
|
+
next context if result == true
|
61
|
+
|
62
|
+
context.fail!("Failed to push #{context.folder_task}")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Wordmove
|
2
|
+
module Actions
|
3
|
+
# Download a single file from the remote server.
|
4
|
+
#
|
5
|
+
# @note The remote server is already configured inside the Photocopier object
|
6
|
+
# @note This action is *not* meant to be organized, but as a standalone one.
|
7
|
+
class GetFile
|
8
|
+
extend LightService::Action
|
9
|
+
include Wordmove::Actions::Helpers
|
10
|
+
|
11
|
+
expects :photocopier,
|
12
|
+
:logger,
|
13
|
+
:cli_options,
|
14
|
+
:command_args
|
15
|
+
|
16
|
+
# @!method execute
|
17
|
+
# @param photocopier [Photocopier::SSH|Photocopier::FTP]
|
18
|
+
# @param logger [Wordmove::Logger]
|
19
|
+
# @param cli_options [Hash] Command line options (with symbolized keys)
|
20
|
+
# @param command_args ((String) remote file path, (String) local file path)
|
21
|
+
# @!scope class
|
22
|
+
# @return [LightService::Context] Action's context
|
23
|
+
executed do |context|
|
24
|
+
command = 'get'
|
25
|
+
|
26
|
+
context.logger.task_step false, "#{command}: #{context.command_args.join(' ')}"
|
27
|
+
|
28
|
+
next context if simulate?(cli_options: context.cli_options)
|
29
|
+
|
30
|
+
result = context.photocopier.send(command, *context.command_args)
|
31
|
+
|
32
|
+
next context if result == true
|
33
|
+
|
34
|
+
context.fail! "Failed to download file: #{context.command_args.first}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
module Wordmove
|
2
|
+
module Actions
|
3
|
+
# Helpers for +Wordmove::Actions+
|
4
|
+
#
|
5
|
+
# All helpers methos are class methods; this way we force avoiding the use
|
6
|
+
# of persistence. All actions have to be approached more as functional code
|
7
|
+
# than OO code. Thus helpers are condidered as functional code too.
|
8
|
+
module Helpers
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
# rubocop:disable Metrics/BlockLength
|
12
|
+
class_methods do
|
13
|
+
# Determines if we're running a simulated command. Actually this is a
|
14
|
+
# wrapper around command line arguments set by the user.
|
15
|
+
#
|
16
|
+
# @param cli_options [Hash] Command line options hash (deep symbolized).
|
17
|
+
# Generally you will find this into action's context
|
18
|
+
# @return [Boolean]
|
19
|
+
# @!scope class
|
20
|
+
def simulate?(cli_options:)
|
21
|
+
cli_options.fetch(:simulate, false)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the path to be excluded as per movefile.yml configuration.
|
25
|
+
# `remote_options` is always valid for both push and pull actions,
|
26
|
+
# because path exclusions are configured only on remote environments
|
27
|
+
#
|
28
|
+
# @param remote_options [Hash] The options hash for the selected remote
|
29
|
+
# remote environment. Generally you will find this into action's context.
|
30
|
+
# @return [Array<String>]
|
31
|
+
# @!scope class
|
32
|
+
def paths_to_exclude(remote_options:)
|
33
|
+
remote_options.fetch(:exclude, [])
|
34
|
+
end
|
35
|
+
|
36
|
+
# Given a path, it will append the `/*` string to it. This is how
|
37
|
+
# folder content - thus not the folder itself - is represented by rsync.
|
38
|
+
# The name of this method is not explicative nor expressive, but we retain
|
39
|
+
# it for backward compatibility.
|
40
|
+
#
|
41
|
+
# @param path [String]
|
42
|
+
# @return [String]
|
43
|
+
# @!scope class
|
44
|
+
def exclude_dir_contents(path:)
|
45
|
+
"#{path}/*"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Construct the mysql dump command as a string
|
49
|
+
#
|
50
|
+
# @param env_db_options [Hash] This hash is defined by the user through movefile.yml
|
51
|
+
# @param save_to_path [String] The path where the db dump will be saved
|
52
|
+
# @return [String] The full composed mysql command
|
53
|
+
# @!scope class
|
54
|
+
def mysql_dump_command(env_db_options:, save_to_path:)
|
55
|
+
command = ['mysqldump']
|
56
|
+
|
57
|
+
if env_db_options[:host].present?
|
58
|
+
command << "--host=#{Shellwords.escape(env_db_options[:host])}"
|
59
|
+
end
|
60
|
+
|
61
|
+
if env_db_options[:port].present?
|
62
|
+
command << "--port=#{Shellwords.escape(env_db_options[:port])}"
|
63
|
+
end
|
64
|
+
|
65
|
+
if env_db_options[:user].present?
|
66
|
+
command << "--user=#{Shellwords.escape(env_db_options[:user])}"
|
67
|
+
end
|
68
|
+
|
69
|
+
if env_db_options[:password].present?
|
70
|
+
command << "--password=#{Shellwords.escape(env_db_options[:password])}"
|
71
|
+
end
|
72
|
+
|
73
|
+
command << "--result-file=\"#{save_to_path}\""
|
74
|
+
|
75
|
+
if env_db_options[:mysqldump_options].present?
|
76
|
+
command << Shellwords.split(env_db_options[:mysqldump_options])
|
77
|
+
end
|
78
|
+
|
79
|
+
command << Shellwords.escape(env_db_options[:name])
|
80
|
+
|
81
|
+
command.join(' ')
|
82
|
+
end
|
83
|
+
|
84
|
+
# Construct the mysql import command as a string
|
85
|
+
#
|
86
|
+
# @param dump_path [String] The path where the dump to import is located
|
87
|
+
# @param env_db_options [Hash] This hash is defined by the user through movefile.yml
|
88
|
+
# @return [String] The full composed mysql command
|
89
|
+
# @!scope class
|
90
|
+
def mysql_import_command(dump_path:, env_db_options:)
|
91
|
+
command = ['mysql']
|
92
|
+
%i[host port user].each do |option|
|
93
|
+
if env_db_options[option].present?
|
94
|
+
command << "--#{option}=#{Shellwords.escape(env_db_options[option])}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
if env_db_options[:password].present?
|
98
|
+
command << "--password=#{Shellwords.escape(env_db_options[:password])}"
|
99
|
+
end
|
100
|
+
command << "--database=#{Shellwords.escape(env_db_options[:name])}"
|
101
|
+
if env_db_options[:mysql_options].present?
|
102
|
+
command << Shellwords.split(env_db_options[:mysql_options])
|
103
|
+
end
|
104
|
+
command << "--execute=\"SET autocommit=0;SOURCE #{dump_path};COMMIT\""
|
105
|
+
command.join(' ')
|
106
|
+
end
|
107
|
+
|
108
|
+
# Construct the command to compress a file as a string. The command will be wrapped
|
109
|
+
# as argument to the +nice+ command, in order to lower the process priority and do
|
110
|
+
# not lock the system while compressing large files.
|
111
|
+
#
|
112
|
+
# @param file_path [String] The path where the file to be compressed is located
|
113
|
+
# @return [String] the command
|
114
|
+
# @!scope class
|
115
|
+
def compress_command(file_path:)
|
116
|
+
command = ['nice']
|
117
|
+
command << '-n'
|
118
|
+
command << '0'
|
119
|
+
command << 'gzip'
|
120
|
+
command << '-9'
|
121
|
+
command << '-f'
|
122
|
+
command << "\"#{file_path}\""
|
123
|
+
command.join(' ')
|
124
|
+
end
|
125
|
+
|
126
|
+
# Construct the command to deflate a compressed file as a string.
|
127
|
+
#
|
128
|
+
# @param file_path [String] The path where the file to be deflated is located
|
129
|
+
# @return [String] the command
|
130
|
+
# @!scope class
|
131
|
+
def uncompress_command(file_path:)
|
132
|
+
command = ['gzip']
|
133
|
+
command << '-d'
|
134
|
+
command << '-f'
|
135
|
+
command << "\"#{file_path}\""
|
136
|
+
command.join(' ')
|
137
|
+
end
|
138
|
+
end
|
139
|
+
# rubocop:enable Metrics/BlockLength
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Wordmove
|
2
|
+
module Actions
|
3
|
+
# Upload a single file to the remote server.
|
4
|
+
#
|
5
|
+
# @note The remote server is already configured inside the Photocopier object
|
6
|
+
# @note This action is *not* meant to be organized, but as a standalone one.
|
7
|
+
class PutFile
|
8
|
+
extend LightService::Action
|
9
|
+
include Wordmove::Actions::Helpers
|
10
|
+
|
11
|
+
expects :photocopier,
|
12
|
+
:logger,
|
13
|
+
:command_args,
|
14
|
+
:cli_options
|
15
|
+
|
16
|
+
# @!method execute
|
17
|
+
# @param photocopier [Photocopier]
|
18
|
+
# @param logger [Wordmove::Logger]
|
19
|
+
# @param command_args ((String) local file path, (String) remote file path)
|
20
|
+
# @return [LightService::Context] Action's context
|
21
|
+
executed do |context|
|
22
|
+
command = 'put'
|
23
|
+
|
24
|
+
# First argument could be a file or a content string. Do not log if the latter
|
25
|
+
message = if File.exist?(context.command_args.first)
|
26
|
+
context.command_args.join(' ')
|
27
|
+
else
|
28
|
+
context.command_args.second
|
29
|
+
end
|
30
|
+
|
31
|
+
context.logger.task_step false, "#{command}: #{message}"
|
32
|
+
|
33
|
+
result = if simulate?(cli_options: context.cli_options)
|
34
|
+
true
|
35
|
+
else
|
36
|
+
context.photocopier.send(command, *context.command_args)
|
37
|
+
end
|
38
|
+
|
39
|
+
next context if result == true
|
40
|
+
# We can't trust the return from the fotocopier method when using FTP. Keep on
|
41
|
+
# and have faith.
|
42
|
+
next context if context.photocopier.is_a? Photocopier::FTP
|
43
|
+
|
44
|
+
context.fail! "Failed to upload file: #{context.command_args.first}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Wordmove
|
2
|
+
module Actions
|
3
|
+
# Runs before push hooks by invoking the external service
|
4
|
+
# Wordmove::Hook
|
5
|
+
class RunAfterPullHook
|
6
|
+
extend ::LightService::Action
|
7
|
+
include Wordmove::Actions::Helpers
|
8
|
+
|
9
|
+
expects :movefile,
|
10
|
+
:cli_options
|
11
|
+
|
12
|
+
# @!method execute
|
13
|
+
# @param movefile [Wordmove::Movefile]
|
14
|
+
# @param cli_options [Hash]
|
15
|
+
# @return [LightService::Context] Action's context
|
16
|
+
executed do |context|
|
17
|
+
Wordmove::Hook.run(
|
18
|
+
:pull,
|
19
|
+
:after,
|
20
|
+
movefile: context.movefile,
|
21
|
+
simulate: simulate?(cli_options: context.cli_options)
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|