wordmove 5.2.1 → 6.0.0.alpha.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +12 -5
  3. data/.rubocop.yml +2 -5
  4. data/.ruby-version +1 -1
  5. data/.yardopts +1 -0
  6. data/CONTRIBUTING.md +15 -0
  7. data/Rakefile +4 -3
  8. data/exe/wordmove +2 -1
  9. data/lib/wordmove/actions/adapt_local_db.rb +103 -0
  10. data/lib/wordmove/actions/adapt_remote_db.rb +90 -0
  11. data/lib/wordmove/actions/backup_local_db.rb +58 -0
  12. data/lib/wordmove/actions/delete_local_file.rb +34 -0
  13. data/lib/wordmove/actions/delete_remote_file.rb +43 -0
  14. data/lib/wordmove/actions/filter_and_setup_tasks_to_run.rb +42 -0
  15. data/lib/wordmove/actions/ftp/backup_remote_db.rb +56 -0
  16. data/lib/wordmove/actions/ftp/cleanup_after_adapt.rb +72 -0
  17. data/lib/wordmove/actions/ftp/download_remote_db.rb +71 -0
  18. data/lib/wordmove/actions/ftp/get_directory.rb +67 -0
  19. data/lib/wordmove/actions/ftp/helpers.rb +91 -0
  20. data/lib/wordmove/actions/ftp/pull_wordpress.rb +56 -0
  21. data/lib/wordmove/actions/ftp/push_wordpress.rb +54 -0
  22. data/lib/wordmove/actions/ftp/put_and_import_dump_remotely.rb +83 -0
  23. data/lib/wordmove/actions/ftp/put_directory.rb +67 -0
  24. data/lib/wordmove/actions/get_file.rb +38 -0
  25. data/lib/wordmove/actions/helpers.rb +142 -0
  26. data/lib/wordmove/actions/put_file.rb +48 -0
  27. data/lib/wordmove/actions/run_after_pull_hook.rb +26 -0
  28. data/lib/wordmove/actions/run_after_push_hook.rb +26 -0
  29. data/lib/wordmove/actions/run_before_pull_hook.rb +26 -0
  30. data/lib/wordmove/actions/run_before_push_hook.rb +26 -0
  31. data/lib/wordmove/actions/run_local_command.rb +34 -0
  32. data/lib/wordmove/actions/setup_context_for_db.rb +78 -0
  33. data/lib/wordmove/actions/ssh/backup_remote_db.rb +51 -0
  34. data/lib/wordmove/actions/ssh/cleanup_after_adapt.rb +56 -0
  35. data/lib/wordmove/actions/ssh/download_remote_db.rb +83 -0
  36. data/lib/wordmove/actions/ssh/get_directory.rb +76 -0
  37. data/lib/wordmove/actions/ssh/helpers.rb +128 -0
  38. data/lib/wordmove/actions/ssh/pull_wordpress.rb +56 -0
  39. data/lib/wordmove/actions/ssh/push_wordpress.rb +54 -0
  40. data/lib/wordmove/actions/ssh/put_and_import_dump_remotely.rb +77 -0
  41. data/lib/wordmove/actions/ssh/put_directory.rb +76 -0
  42. data/lib/wordmove/actions/ssh/run_remote_command.rb +39 -0
  43. data/lib/wordmove/assets/dump.php.erb +6 -6
  44. data/lib/wordmove/assets/import.php.erb +7 -7
  45. data/lib/wordmove/assets/wordmove_schema_global.yml +2 -0
  46. data/lib/wordmove/assets/wordmove_schema_remote.yml +2 -2
  47. data/lib/wordmove/cli.rb +152 -91
  48. data/lib/wordmove/db_paths_config.rb +44 -0
  49. data/lib/wordmove/doctor/movefile.rb +8 -8
  50. data/lib/wordmove/doctor/mysql.rb +18 -15
  51. data/lib/wordmove/doctor/rsync.rb +2 -2
  52. data/lib/wordmove/doctor/ssh.rb +3 -3
  53. data/lib/wordmove/doctor/wpcli.rb +5 -5
  54. data/lib/wordmove/environments_list.rb +4 -4
  55. data/lib/wordmove/exceptions.rb +13 -0
  56. data/lib/wordmove/generators/movefile.rb +7 -5
  57. data/lib/wordmove/generators/movefile_adapter.rb +11 -5
  58. data/lib/wordmove/guardian.rb +5 -5
  59. data/lib/wordmove/hook.rb +13 -14
  60. data/lib/wordmove/logger.rb +11 -10
  61. data/lib/wordmove/movefile.rb +63 -59
  62. data/lib/wordmove/organizers/ftp/pull.rb +52 -0
  63. data/lib/wordmove/organizers/ftp/push.rb +53 -0
  64. data/lib/wordmove/organizers/ssh/pull.rb +52 -0
  65. data/lib/wordmove/organizers/ssh/push.rb +53 -0
  66. data/lib/wordmove/version.rb +1 -1
  67. data/lib/wordmove/wordpress_directory.rb +76 -8
  68. data/lib/wordmove/wpcli.rb +88 -0
  69. data/lib/wordmove.rb +33 -11
  70. data/wordmove.gemspec +37 -30
  71. metadata +139 -35
  72. data/lib/wordmove/deployer/base.rb +0 -193
  73. data/lib/wordmove/deployer/ftp.rb +0 -160
  74. data/lib/wordmove/deployer/ssh/default_sql_adapter.rb +0 -47
  75. data/lib/wordmove/deployer/ssh/wpcli_sql_adapter.rb +0 -55
  76. data/lib/wordmove/deployer/ssh.rb +0 -169
  77. data/lib/wordmove/sql_adapter/default.rb +0 -68
  78. data/lib/wordmove/sql_adapter/wpcli.rb +0 -54
  79. data/lib/wordmove/wordpress_directory/path.rb +0 -11
@@ -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
@@ -0,0 +1,26 @@
1
+ module Wordmove
2
+ module Actions
3
+ # Runs after push hooks by invoking the external service
4
+ # Wordmove::Hook
5
+ class RunAfterPushHook
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
+ :push,
19
+ :after,
20
+ movefile: context.movefile,
21
+ simulate: simulate?(cli_options: context.cli_options)
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module Wordmove
2
+ module Actions
3
+ # Runs before pull hooks by invoking the external service
4
+ # Wordmove::Hook
5
+ class RunBeforePullHook
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
+ :before,
20
+ movefile: context.movefile,
21
+ simulate: simulate?(cli_options: context.cli_options)
22
+ )
23
+ end
24
+ end
25
+ end
26
+ 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 RunBeforePushHook
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
+ :push,
19
+ :before,
20
+ movefile: context.movefile,
21
+ simulate: simulate?(cli_options: context.cli_options)
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,34 @@
1
+ require 'English'
2
+
3
+ module Wordmove
4
+ module Actions
5
+ # Run a command on the local system.
6
+ # Command won't be run if +--simulate+ flag is present on CLI.
7
+ # @note This action is *not* meant to be organized, but as a standalone one.
8
+ class RunLocalCommand
9
+ extend LightService::Action
10
+ include Wordmove::Actions::Helpers
11
+
12
+ expects :command,
13
+ :cli_options,
14
+ :logger
15
+
16
+ # @!method execute
17
+ # @param command [String] The command to run
18
+ # @param cli_options [Hash]
19
+ # @param logger [Wordmove::Logger]
20
+ # @return [LightService::Context] Action's context
21
+ executed do |context|
22
+ context.logger.task_step true, context.command
23
+
24
+ next context if simulate?(cli_options: context.cli_options)
25
+
26
+ begin
27
+ system(context.command, exception: true)
28
+ rescue RuntimeError, SystemExit => e
29
+ context.fail!("Local command status reports an error: #{e.message}")
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,78 @@
1
+ module Wordmove
2
+ module Actions
3
+ class SetupContextForDb
4
+ extend ::LightService::Action
5
+ include Wordmove::Actions::Helpers
6
+ include Wordmove::Actions::Ftp::Helpers
7
+ include WordpressDirectory::LocalHelperMethods
8
+ include WordpressDirectory::RemoteHelperMethods
9
+
10
+ expects :cli_options,
11
+ :local_options,
12
+ :remote_options,
13
+ :logger,
14
+ :movefile,
15
+ :database_task
16
+ promises :db_paths
17
+
18
+ executed do |context| # rubocop:disable Metrics/BlockLength
19
+ if context.database_task == false
20
+ context.db_paths = false
21
+ next context
22
+ end
23
+
24
+ next context if simulate?(cli_options: context.cli_options)
25
+
26
+ content_dir = local_wp_content_dir(local_options: context.local_options)
27
+
28
+ token = remote_php_scripts_token
29
+
30
+ DbPathsConfig.local.path = content_dir.path('dump.sql')
31
+ DbPathsConfig.local.gzipped_path = "#{DbPathsConfig.local.path}.gz"
32
+ DbPathsConfig.remote.path = remote_wp_content_dir(
33
+ remote_options: context.remote_options
34
+ ).path('dump.sql')
35
+ DbPathsConfig.remote.gzipped_path = "#{DbPathsConfig.remote.path}.gz"
36
+ DbPathsConfig.local.adapted_path = content_dir.path('search_replace_dump.sql')
37
+ DbPathsConfig.local.gzipped_adapted_path = "#{DbPathsConfig.local.adapted_path}.gz"
38
+ DbPathsConfig.backup.local.path = content_dir.path("local-backup-#{Time.now.to_i}.sql")
39
+ DbPathsConfig.backup.local.gzipped_path = "#{DbPathsConfig.backup.local.path}.gz"
40
+ DbPathsConfig.backup.remote.path =
41
+ content_dir.path("#{context.movefile.environment}-backup-#{Time.now.to_i}.sql")
42
+ DbPathsConfig.backup.remote.gzipped_path = "#{DbPathsConfig.backup.remote.path}.gz"
43
+
44
+ DbPathsConfig.ftp.remote.dump_script_path = remote_wp_content_dir(
45
+ remote_options: context.remote_options
46
+ ).path('dump.php')
47
+ DbPathsConfig.ftp.remote.dumped_path = remote_wp_content_dir(
48
+ remote_options: context.remote_options
49
+ ).path('dump.mysql')
50
+ DbPathsConfig.ftp.remote.dump_script_url = remote_wp_content_dir(
51
+ remote_options: context.remote_options
52
+ ).url('dump.php')
53
+ DbPathsConfig.ftp.remote.import_script_path = remote_wp_content_dir(
54
+ remote_options: context.remote_options
55
+ ).path('import.php')
56
+ DbPathsConfig.ftp.remote.import_script_url = remote_wp_content_dir(
57
+ remote_options: context.remote_options
58
+ ).url('import.php')
59
+ DbPathsConfig.ftp.local.generated_dump_script_path = generate_dump_script(
60
+ remote_db_options: context.remote_options[:database],
61
+ token: token
62
+ )
63
+ DbPathsConfig.ftp.local.generated_import_script_path = generate_import_script(
64
+ remote_db_options: context.remote_options[:database],
65
+ token: token
66
+ )
67
+ DbPathsConfig.ftp.local.temp_path = local_wp_content_dir(
68
+ local_options: context.local_options
69
+ ).path('log.html')
70
+ # I know this is not a path, but it's used to generate
71
+ # a URL to dump the DB, so it's somewhat in context
72
+ DbPathsConfig.ftp.token = token
73
+
74
+ context.db_paths = DbPathsConfig
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,51 @@
1
+ module Wordmove
2
+ module Actions
3
+ module Ssh
4
+ # Bakups an alrady downloaded remote dump
5
+ class BackupRemoteDb
6
+ extend ::LightService::Action
7
+ include Wordmove::Actions::Helpers
8
+
9
+ expects :cli_options,
10
+ :logger,
11
+ :db_paths
12
+
13
+ # @!method execute
14
+ # @param cli_options [Hash] Command line options (with symbolized keys)
15
+ # @param logger [Wordmove::Logger]
16
+ # @param db_paths [BbPathsConfig] Configuration object for database
17
+ # @!scope class
18
+ # @return [LightService::Context] Action's context
19
+ executed do |context|
20
+ next context if context.database_task == false
21
+
22
+ context.logger.task 'Backup remote DB'
23
+
24
+ if simulate?(cli_options: context.cli_options)
25
+ context.logger.info 'A backup of the remote DB would have been saved into ' \
26
+ "#{context.db_paths.backup.remote.gzipped_path}, " \
27
+ 'but you\'re simulating'
28
+ next context
29
+ end
30
+
31
+ # Most of the expectations are needed to be proxied to `DownloadRemoteDb`
32
+ # Wordmove::Actions::Ssh::DownloadRemoteDb.execute(context)
33
+ # DownloadRemoteDB will save the file in `db_paths.local.gzipped_path`
34
+
35
+ begin
36
+ FileUtils.mv(
37
+ context.db_paths.local.gzipped_path,
38
+ context.db_paths.backup.remote.gzipped_path
39
+ )
40
+
41
+ context.logger.success(
42
+ "Backup saved at #{context.db_paths.backup.remote.gzipped_path}"
43
+ )
44
+ rescue Errno::ENOENT => e
45
+ context.fail_and_return!("Remote DB backup failed with: #{e.message}. Aborting.")
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,56 @@
1
+ module Wordmove
2
+ module Actions
3
+ module Ssh
4
+ # Cleanup file created during DB push/pull operations
5
+ class CleanupAfterAdapt
6
+ extend ::LightService::Action
7
+ include Wordmove::Actions::Helpers
8
+
9
+ expects :db_paths,
10
+ :cli_options,
11
+ :logger
12
+
13
+ # @!method execute
14
+ # @param db_paths [BbPathsConfig] Configuration object for database
15
+ # @param cli_options [Hash] Command line options (with symbolized keys)
16
+ # @param logger [Wordmove::Logger]
17
+ # @!scope class
18
+ # @return [LightService::Context] Action's context
19
+ executed do |context| # rubocop:disable Metrics/BlockLength
20
+ next context if context.database_task == false
21
+
22
+ context.logger.task 'Cleanup'
23
+
24
+ if simulate?(cli_options: context.cli_options)
25
+ context.logger.info 'No cleanup during simulation'
26
+ next context
27
+ end
28
+
29
+ result = Wordmove::Actions::DeleteLocalFile.execute(
30
+ logger: context.logger,
31
+ cli_options: context.cli_options,
32
+ file_path: context.db_paths.local.path
33
+ )
34
+ if result.failure?
35
+ context.logger.warning 'Failed to delete local file ' \
36
+ "#{context.db_paths.local.path} because: " \
37
+ "#{result.message}" \
38
+ '. Manual intervention required'
39
+ end
40
+
41
+ result = Wordmove::Actions::DeleteLocalFile.execute(
42
+ cli_options: context.cli_options,
43
+ logger: context.logger,
44
+ file_path: context.db_paths.local.gzipped_adapted_path
45
+ )
46
+ if result.failure?
47
+ context.logger.warning 'Failed to delete local file ' \
48
+ "#{context.db_paths.local.gzipped_adapted_path} because: " \
49
+ "#{result.message}" \
50
+ '. Manual intervention required'
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,83 @@
1
+ module Wordmove
2
+ module Actions
3
+ module Ssh
4
+ # Downloads the remote DB over SSH protocol
5
+ class DownloadRemoteDb
6
+ extend ::LightService::Action
7
+ include Wordmove::Actions::Helpers
8
+ include WordpressDirectory::LocalHelperMethods
9
+ include WordpressDirectory::RemoteHelperMethods
10
+
11
+ expects :remote_options,
12
+ :cli_options,
13
+ :logger,
14
+ :photocopier,
15
+ :db_paths
16
+
17
+ # @!method execute
18
+ # @param remote_options [Hash] Remote host options fetched from
19
+ # movefile (with symbolized keys)
20
+ # @param cli_options [Hash] Command line options (with symbolized keys)
21
+ # @param logger [Wordmove::Logger]
22
+ # @param photocopier [Photocopier::SSH]
23
+ # @param db_paths [BbPathsConfig] Configuration object for database
24
+ # @!scope class
25
+ # @return [LightService::Context] Action's context
26
+ executed do |context| # rubocop:disable Metrics/BlockLength
27
+ next context if context.database_task == false
28
+
29
+ context.logger.task 'Download remote DB'
30
+
31
+ next context if simulate?(cli_options: context.cli_options)
32
+
33
+ result = Wordmove::Actions::Ssh::RunRemoteCommand.execute(
34
+ cli_options: context.cli_options,
35
+ photocopier: context.photocopier,
36
+ logger: context.logger,
37
+ command: mysql_dump_command(
38
+ env_db_options: context.remote_options[:database],
39
+ save_to_path: context.db_paths.remote.path
40
+ )
41
+ )
42
+ context.fail_and_return!(result.message) if result.failure?
43
+
44
+ result = Wordmove::Actions::Ssh::RunRemoteCommand.execute(
45
+ cli_options: context.cli_options,
46
+ photocopier: context.photocopier,
47
+ logger: context.logger,
48
+ command: compress_command(file_path: context.db_paths.remote.path)
49
+ )
50
+ context.fail_and_return!(result.message) if result.failure?
51
+
52
+ result = Wordmove::Actions::GetFile.execute(
53
+ photocopier: context.photocopier,
54
+ logger: context.logger,
55
+ cli_options: context.cli_options,
56
+ command_args: [
57
+ context.db_paths.remote.gzipped_path,
58
+ context.db_paths.local.gzipped_path
59
+ ]
60
+ )
61
+ context.fail_and_return!(result.message) if result.failure?
62
+
63
+ result = Wordmove::Actions::DeleteRemoteFile.execute(
64
+ photocopier: context.photocopier,
65
+ logger: context.logger,
66
+ cli_options: context.cli_options,
67
+ remote_file: context.db_paths.remote.gzipped_path
68
+ )
69
+ if result.failure?
70
+ context.logger.warning 'Failed to delete remote file ' \
71
+ "#{context.db_paths.remote.gzipped_path} because: " \
72
+ "#{result.message}" \
73
+ '. Manual intervention required'
74
+ end
75
+
76
+ context.logger.success(
77
+ "Remote DB dump downloaded in #{context.db_paths.local.gzipped_path}"
78
+ )
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,76 @@
1
+ module Wordmove
2
+ module Actions
3
+ module Ssh
4
+ # Syncs a whole directory over SSH protocol from the remote server to local host
5
+ class GetDirectory
6
+ extend LightService::Action
7
+ include Wordmove::Actions::Helpers
8
+ include Wordmove::Actions::Ssh::Helpers
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::SSH]
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
+ # For this action `local_path` and `remote_path` will always be
37
+ # `:wordpress_path`; specific folder for `context.folder_task` will be included by
38
+ # `pull_include_paths`
39
+ local_path = context.local_options[:wordpress_path]
40
+ remote_path = context.remote_options[:wordpress_path]
41
+
42
+ # This action can generate `command_args` by itself,
43
+ # but it gives the context the chance to ovveride it.
44
+ # By the way this variable is not `expects`ed.
45
+ # Note that we do not use the second argument to `fetch`
46
+ # to express a default value, because it would be greedly interpreted
47
+ # but if `command_args` is already defined by context, then it's
48
+ # possible that `"remote_#{context.folder_task}_dir"` could
49
+ # not be defined.
50
+ command_args = context.fetch(:command_args) || [
51
+ remote_path,
52
+ local_path,
53
+ pull_exclude_paths(
54
+ remote_task_dir: send(
55
+ "remote_#{context.folder_task}_dir",
56
+ remote_options: context.remote_options
57
+ ),
58
+ paths_to_exclude: paths_to_exclude(remote_options: context.remote_options)
59
+ ),
60
+ pull_include_paths(remote_task_dir: send(
61
+ "remote_#{context.folder_task}_dir",
62
+ remote_options: context.remote_options
63
+ ))
64
+ ]
65
+
66
+ context.logger.task_step false, "#{command}: #{command_args.join(' ')}"
67
+ result = context.photocopier.send(command, *command_args)
68
+
69
+ next context if result == true
70
+
71
+ context.fail!("Failed to push #{context.folder_task}")
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,128 @@
1
+ require 'pathname'
2
+
3
+ # rubocop:disable Metrics/BlockLength
4
+ module Wordmove
5
+ module Actions
6
+ module Ssh
7
+ module Helpers
8
+ extend ActiveSupport::Concern
9
+
10
+ class_methods do
11
+ #
12
+ # Utility method to retrieve and augment ssh options from the superset of remote options.
13
+ # This is useful most because it appends +--dy-run+ rsync's flag to ssh options based
14
+ # on +--simulate+ flag presence
15
+ #
16
+ # @param [Hash] remote_options Remote host options fetcehd from movefile
17
+ # @param [Bool] simulate Tell the moethod if you're in a simulated operation
18
+ #
19
+ # @return [Hash] Ssh options
20
+ #
21
+ def ssh_options(remote_options:, simulate: false)
22
+ ssh_options = remote_options[:ssh]
23
+
24
+ if simulate == true && ssh_options[:rsync_options]
25
+ ssh_options[:rsync_options].concat(' --dry-run')
26
+ elsif simulate == true
27
+ ssh_options[:rsync_options] = '--dry-run'
28
+ end
29
+
30
+ ssh_options
31
+ end
32
+
33
+ #
34
+ # Given the directory you're pushing/pulling, generates an array of path to be included
35
+ # by rsync while pushing. Note that by design include paths are always required but are
36
+ # only programmatically deduced and never user configured.
37
+ #
38
+ # @note The business logic behind how these paths are produced should be deepened
39
+ #
40
+ # @param [WordpressDirectory] local_task_dir An object representing a wordpress folder
41
+ #
42
+ # @return [Array<String>] The array of path to be included by rsync
43
+ #
44
+ def push_include_paths(local_task_dir:)
45
+ Pathname.new(local_task_dir.relative_path)
46
+ .ascend
47
+ .each_with_object([]) do |directory, array|
48
+ path = directory.to_path
49
+ path.prepend('/') unless path.match? %r{^/}
50
+ path.concat('/') unless path.match? %r{/$}
51
+ array << path
52
+ end
53
+ end
54
+
55
+ #
56
+ # Given the directory you're pushing/pulling and the user configured exclude list,
57
+ # generates an array of path to be excluded
58
+ # by rsync while pushing. Note that by design exclude some paths are always required
59
+ # even when the user does not confiure any exclusion.
60
+ #
61
+ # @note The business logic behind how these paths are produced should be deepened
62
+ #
63
+ # @param [WordpressDirectory] local_task_dir An object representing a wordpress folder
64
+ # @param [Array<String>] pats_to_exclude An array of paths
65
+ #
66
+ # @return [Array<String>] The array of path to be included by rsync
67
+ #
68
+ def push_exclude_paths(local_task_dir:, paths_to_exclude:)
69
+ Pathname.new(local_task_dir.relative_path)
70
+ .dirname
71
+ .ascend
72
+ .each_with_object([]) do |directory, array|
73
+ path = directory.to_path
74
+ path.prepend('/') unless path.match? %r{^/}
75
+ path.concat('/') unless path.match? %r{/$}
76
+ path.concat('*')
77
+ array << path
78
+ end
79
+ .concat(paths_to_exclude)
80
+ .concat(['/*'])
81
+ end
82
+
83
+ #
84
+ # Same as Wordmove::Actions::Ssh::Helpers.push_include_path but for pull actions
85
+ #
86
+ # @param [WordpressDirectory] local_task_dir An object representing a wordpress folder
87
+ #
88
+ # @return [Array<String>] An array of paths
89
+ #
90
+ def pull_include_paths(remote_task_dir:)
91
+ Pathname.new(remote_task_dir.relative_path)
92
+ .ascend
93
+ .each_with_object([]) do |directory, array|
94
+ path = directory.to_path
95
+ path.prepend('/') unless path.match? %r{^/}
96
+ path.concat('/') unless path.match? %r{/$}
97
+ array << path
98
+ end
99
+ end
100
+
101
+ #
102
+ # Same as Wordmove::Actions::Ssh::Helpers.push_exclude_path but for pull actions
103
+ #
104
+ # @param [WordpressDirectory] local_task_dir An object representing a wordpress folder
105
+ # @param [Array<String>] paths_to_exclude User configured array of paths to exclude
106
+ #
107
+ # @return [Array<String>] Array of paths to be excluded
108
+ #
109
+ def pull_exclude_paths(remote_task_dir:, paths_to_exclude:)
110
+ Pathname.new(remote_task_dir.relative_path)
111
+ .dirname
112
+ .ascend
113
+ .each_with_object([]) do |directory, array|
114
+ path = directory.to_path
115
+ path.prepend('/') unless path.match? %r{^/}
116
+ path.concat('/') unless path.match? %r{/$}
117
+ path.concat('*')
118
+ array << path
119
+ end
120
+ .concat(paths_to_exclude)
121
+ .concat(['/*'])
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ # rubocop:enable Metrics/BlockLength