wordmove 5.2.1 → 6.0.0.alpha.3

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 (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