dkdeploy-core 8.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.rubocop.yml +18 -0
  4. data/Berksfile +3 -0
  5. data/Berksfile.lock +46 -0
  6. data/CONTRIBUTORS.md +16 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +7 -0
  9. data/README.md +88 -0
  10. data/Rakefile +5 -0
  11. data/Vagrantfile +60 -0
  12. data/config/vm/cookbooks/dkdeploy-core/metadata.rb +10 -0
  13. data/config/vm/cookbooks/dkdeploy-core/recipes/default.rb +56 -0
  14. data/dkdeploy-core.gemspec +34 -0
  15. data/features/apache.feature +35 -0
  16. data/features/assets.feature +100 -0
  17. data/features/bower.feature +52 -0
  18. data/features/current_folder.feature +17 -0
  19. data/features/db.feature +93 -0
  20. data/features/deploy.feature +48 -0
  21. data/features/enhanced_symlinks.feature +17 -0
  22. data/features/error_handlers.feature +59 -0
  23. data/features/file_access.feature +120 -0
  24. data/features/maintenance.feature +25 -0
  25. data/features/project_version.feature +42 -0
  26. data/features/support/env.rb +22 -0
  27. data/features/utils.feature +81 -0
  28. data/lib/capistrano/copy.rb +2 -0
  29. data/lib/capistrano/dkdeploy/core.rb +88 -0
  30. data/lib/dkdeploy/constants.rb +156 -0
  31. data/lib/dkdeploy/copy.rb +121 -0
  32. data/lib/dkdeploy/core/version.rb +15 -0
  33. data/lib/dkdeploy/dsl.rb +23 -0
  34. data/lib/dkdeploy/helpers/assets.rb +50 -0
  35. data/lib/dkdeploy/helpers/common.rb +31 -0
  36. data/lib/dkdeploy/helpers/db.rb +49 -0
  37. data/lib/dkdeploy/helpers/file_system.rb +76 -0
  38. data/lib/dkdeploy/i18n.rb +143 -0
  39. data/lib/dkdeploy/interaction_handler/password.rb +27 -0
  40. data/lib/dkdeploy/rollback_manager.rb +18 -0
  41. data/lib/dkdeploy/tasks/apache.rake +29 -0
  42. data/lib/dkdeploy/tasks/assets.rake +96 -0
  43. data/lib/dkdeploy/tasks/bower.rake +54 -0
  44. data/lib/dkdeploy/tasks/copy.rake +26 -0
  45. data/lib/dkdeploy/tasks/current_folder.rake +16 -0
  46. data/lib/dkdeploy/tasks/db.rake +412 -0
  47. data/lib/dkdeploy/tasks/deploy.rake +77 -0
  48. data/lib/dkdeploy/tasks/enhanced_symlinks.rake +74 -0
  49. data/lib/dkdeploy/tasks/fail.rake +8 -0
  50. data/lib/dkdeploy/tasks/file_access.rake +89 -0
  51. data/lib/dkdeploy/tasks/maintenance.rake +73 -0
  52. data/lib/dkdeploy/tasks/project_version.rake +32 -0
  53. data/lib/dkdeploy/tasks/utils.rake +141 -0
  54. data/lib/dkdeploy.rb +1 -0
  55. data/spec/fixtures/application/Capfile +11 -0
  56. data/spec/fixtures/application/Gemfile +11 -0
  57. data/spec/fixtures/application/Version +1 -0
  58. data/spec/fixtures/application/config/assets_exclude_file.txt +1 -0
  59. data/spec/fixtures/application/config/deploy/dev.rb +35 -0
  60. data/spec/fixtures/application/config/deploy.rb +18 -0
  61. data/spec/fixtures/application/config/etc/apache2/conf/.htaccess.erb +12 -0
  62. data/spec/fixtures/application/config/etc/apache2/conf/dev.htaccess.erb +3 -0
  63. data/spec/fixtures/application/config/preseed/default_content.sql.gz +0 -0
  64. data/spec/fixtures/application/config/preseed/default_structure.sql.gz +0 -0
  65. data/spec/fixtures/application/config/preseed/fileadmin.tar.gz +0 -0
  66. data/spec/fixtures/application/config/preseed/uploads.tar.gz +0 -0
  67. data/spec/fixtures/application/htdocs/.hidden/.gitkeep +0 -0
  68. data/spec/fixtures/application/htdocs/Gemfile +0 -0
  69. data/spec/fixtures/application/htdocs/bower.json +15 -0
  70. data/spec/fixtures/application/htdocs/catalog/.hidden/.gitkeep +0 -0
  71. data/spec/fixtures/application/htdocs/catalog/index.html +1 -0
  72. data/spec/fixtures/application/htdocs/index.html +1 -0
  73. data/spec/fixtures/application/htdocs/stylesheets/test1/config.rb +3 -0
  74. data/spec/fixtures/application/htdocs/stylesheets/test1/css/.gitkeep +0 -0
  75. data/spec/fixtures/application/htdocs/stylesheets/test1/src/source.scss +5 -0
  76. data/spec/fixtures/application/htdocs/stylesheets/test2/config.rb +3 -0
  77. data/spec/fixtures/application/htdocs/stylesheets/test2/css/.gitkeep +0 -0
  78. data/spec/fixtures/application/htdocs/stylesheets/test2/src/source.scss +5 -0
  79. data/spec/fixtures/application/temp/dkdeploy_core.sql.gz +0 -0
  80. data/spec/fixtures/capistrano/configuration/add_output_after_create_symlink.rb +7 -0
  81. data/spec/fixtures/capistrano/configuration/custom_compass_sources.rb +4 -0
  82. data/spec/fixtures/capistrano/configuration/custom_file_access.rb +13 -0
  83. data/spec/fixtures/capistrano/configuration/default_deployment_behaviour.rb +9 -0
  84. metadata +346 -0
@@ -0,0 +1,22 @@
1
+ require 'dkdeploy/test_environment/application'
2
+
3
+ ssh_config = {}
4
+
5
+ ssh_key_files = Dir.glob(File.join(Dir.getwd, '.vagrant', 'machines', '**', 'virtualbox', 'private_key'))
6
+ unless ssh_key_files.empty?
7
+ # Define generated ssh key files
8
+ ssh_config = {
9
+ user: 'vagrant',
10
+ keys: ssh_key_files
11
+ }
12
+ end
13
+
14
+ TEST_APPLICATION = Dkdeploy::TestEnvironment::Application.new(File.expand_path('../../../', __FILE__), 'dkdeploy-core.dev', ssh_config)
15
+ TEST_APPLICATION.mysql_connection_settings = { host: 'dkdeploy-core.dev', username: 'root', password: 'ilikerandompasswords' }
16
+
17
+ # this configuration tricks Bundler into executing another Bundler project with clean enviroment
18
+ # The official way via Bundler.with_clean_env did not work properly here
19
+ Aruba.configure do |config|
20
+ config.command_runtime_environment = { 'BUNDLE_GEMFILE' => File.join(TEST_APPLICATION.test_app_path, 'Gemfile') }
21
+ config.exit_timeout = 10
22
+ end
@@ -0,0 +1,81 @@
1
+ Feature: Test tasks for namespace 'utils'
2
+
3
+ Background:
4
+ Given a test app with the default configuration
5
+ And the remote server is cleared
6
+
7
+ Scenario: Upload local file to server
8
+ Given an empty file named "htdocs/new_file.html"
9
+ Then I successfully run `cap dev utils:upload_file['htdocs/new_file.html']`
10
+ Then a remote file named "current_path/new_file.html" should exist
11
+
12
+ Scenario: Upload local file to server which does not exist locally
13
+ And I run `cap dev utils:upload_file['htdocs/new_file.html']`
14
+ Then the exit status should be 1
15
+
16
+ Scenario: Call task "utils:upload_file" without entering any file name
17
+ Given an empty file named "htdocs/new_file.html"
18
+ When I run `cap dev utils:upload_file` interactively
19
+ And I type ""
20
+ And I close the stdin stream
21
+ Then the exit status should be 1
22
+ And a remote file named "current_path/new_file.html" should not exist
23
+
24
+ Scenario: Download remote file
25
+ Given a remote directory named "current_path"
26
+ And a remote file named "current_path/download_file.txt" with:
27
+ """
28
+ Remote file content
29
+ """
30
+ When I successfully run `cap dev utils:download_file['download_file.txt']`
31
+ Then a file named "temp/download_file.dkdeploy-core.dev.txt" should exist
32
+
33
+ Scenario: Download a file from server which does exist locally
34
+ Given a file named "temp/download_file.dkdeploy-core.dev.txt" with:
35
+ """
36
+ Local file content
37
+ """
38
+ And a remote directory named "current_path"
39
+ And a remote file named "current_path/download_file.txt" with:
40
+ """
41
+ Remote file content
42
+ """
43
+ When I successfully run `cap dev utils:download_file['download_file.txt']`
44
+ Then the file "temp/download_file.dkdeploy-core.dev.txt" should contain exactly:
45
+ """
46
+ Remote file content
47
+
48
+ """
49
+
50
+ Scenario: Call task "utils:download_file" without entering any file name
51
+ When I run `cap dev utils:download_file` interactively
52
+ And I type ""
53
+ And I close the stdin stream
54
+ Then the exit status should be 1
55
+
56
+ Scenario: Rsync add new files to server
57
+ Given the project is deployed
58
+ And an empty file named "htdocs/new_file.txt"
59
+ And I successfully run `cap dev utils:rsync`
60
+ Then a remote file named "current_path/new_file.txt" should exist
61
+
62
+ Scenario: Rsync add new files at root to server
63
+ Given the project is deployed
64
+ And I extend the development capistrano configuration variable rsync_path with value '.'
65
+ And an empty file named "new_file.txt"
66
+ And I successfully run `cap dev utils:rsync`
67
+ Then a remote file named "current_path/new_file.txt" should exist
68
+
69
+ Scenario: Rsync exclude files
70
+ Given the project is deployed
71
+ And I extend the development capistrano configuration variable rsync_exclude with value ['file_to_exclude.txt']
72
+ And an empty file named "htdocs/file_to_exclude.txt"
73
+ And I successfully run `cap dev utils:rsync`
74
+ Then a remote file named "current_path/file_to_exclude.txt" should not exist
75
+
76
+ Scenario: Create custom directories within path shared
77
+ When I run `cap dev utils:create_custom_directories` interactively
78
+ And I type "mydirectory"
79
+ And I close the stdin stream
80
+ Then the exit status should be 0
81
+ And a remote directory named "shared_path/mydirectory" should exist
@@ -0,0 +1,2 @@
1
+ # if capistrano scm is set to copy, the current file will be loaded
2
+ load File.expand_path('../../dkdeploy/tasks/copy.rake', __FILE__)
@@ -0,0 +1,88 @@
1
+ include Capistrano::DSL
2
+
3
+ require 'dkdeploy/rollback_manager'
4
+
5
+ # Load dkdeploy tasks
6
+ load File.expand_path('../../../dkdeploy/tasks/deploy.rake', __FILE__)
7
+ load File.expand_path('../../../dkdeploy/tasks/fail.rake', __FILE__)
8
+ load File.expand_path('../../../dkdeploy/tasks/maintenance.rake', __FILE__)
9
+ load File.expand_path('../../../dkdeploy/tasks/utils.rake', __FILE__)
10
+ load File.expand_path('../../../dkdeploy/tasks/file_access.rake', __FILE__)
11
+ load File.expand_path('../../../dkdeploy/tasks/assets.rake', __FILE__)
12
+ load File.expand_path('../../../dkdeploy/tasks/apache.rake', __FILE__)
13
+ load File.expand_path('../../../dkdeploy/tasks/project_version.rake', __FILE__)
14
+ load File.expand_path('../../../dkdeploy/tasks/db.rake', __FILE__)
15
+ load File.expand_path('../../../dkdeploy/tasks/enhanced_symlinks.rake', __FILE__)
16
+ load File.expand_path('../../../dkdeploy/tasks/current_folder.rake', __FILE__)
17
+ load File.expand_path('../../../dkdeploy/tasks/bower.rake', __FILE__)
18
+
19
+ # Hook into symlink related tasks
20
+ after 'deploy:check:linked_dirs', 'deploy:enhanced_symlinks:check:linked_dirs'
21
+ after 'deploy:check:linked_files', 'deploy:enhanced_symlinks:check:linked_files'
22
+ after 'deploy:check:make_linked_dirs', 'deploy:enhanced_symlinks:check:make_linked_dirs'
23
+ after 'deploy:symlink:linked_dirs', 'deploy:enhanced_symlinks:symlink:linked_dirs'
24
+ after 'deploy:symlink:linked_files', 'deploy:enhanced_symlinks:symlink:linked_files'
25
+
26
+ namespace :load do
27
+ task :defaults do
28
+ # Set scm to new scm "copy"
29
+ set :scm, :copy
30
+
31
+ # Set default values for copy scm
32
+ set :copy_source, 'htdocs'
33
+ set :copy_exclude, Array[
34
+ 'vendor/bundle/**',
35
+ 'Gemfile*',
36
+ '**/.git',
37
+ '**/.svn',
38
+ '**/.DS_Store',
39
+ '.settings',
40
+ '.project',
41
+ '.buildpath',
42
+ 'Capfile',
43
+ 'Thumbs.db',
44
+ 'composer.lock'
45
+ ]
46
+
47
+ # Set default web root paths
48
+ set :local_web_root_path, -> { fetch(:copy_source) }
49
+ set :remote_web_root_path, '.'
50
+
51
+ # Set default version file path
52
+ set :version_file_path, ''
53
+
54
+ # default file owner
55
+ set :default_file_access_owner_of_shared_path, 'www-data'
56
+ set :default_file_access_owner_of_release_path, 'www-data'
57
+ # default file group
58
+ set :default_file_access_group_of_shared_path, 'www-data'
59
+ set :default_file_access_group_of_release_path, 'www-data'
60
+ # default file access mode
61
+ set :default_file_access_mode_of_shared_path, 'u+rwX,g+rX,g-w,o-rwx'
62
+ set :default_file_access_mode_of_release_path, 'u+rwX,g+rX,g-w,o-rwx'
63
+ # custom file access properties
64
+ set :custom_file_access, {}
65
+
66
+ # Set default compass sources
67
+ set :compass_sources, []
68
+ set :compass_compile_arguments, []
69
+
70
+ # Number of archives to keep around
71
+ set :keep_rollback_archives, 5
72
+
73
+ # List of bower.json files
74
+ set :bower_path, -> { fetch(:copy_source) }
75
+ set :bower_paths, -> { Array(fetch(:copy_source)) }
76
+
77
+ # List of filters for file_access:set_selected_custom_access
78
+ set :selected_custom_file_access, []
79
+
80
+ # Set assets configuration
81
+ set :asset_default_content, []
82
+ set :asset_exclude_file, ''
83
+ set :asset_folders, []
84
+
85
+ # Airbrush configuration
86
+ set :format_options, command_output: true, log_file: nil, truncate: false
87
+ end
88
+ end
@@ -0,0 +1,156 @@
1
+ module Dkdeploy
2
+ # Global static methods
3
+ #
4
+ module Constants
5
+ #####################################################
6
+ # General constants
7
+ #####################################################
8
+
9
+ # Copy source path
10
+ #
11
+ # @return [String]
12
+ def copy_source
13
+ fetch :copy_source, '.'
14
+ end
15
+
16
+ # Local Dump Path
17
+ #
18
+ # @return [String]
19
+ def local_dump_path
20
+ fetch :local_dump_path, 'temp'
21
+ end
22
+
23
+ # Copy exclude pattern
24
+ #
25
+ # @return [Array]
26
+ def copy_exclude
27
+ fetch :copy_exclude, []
28
+ end
29
+
30
+ # Application name
31
+ #
32
+ # @return [String]
33
+ def application
34
+ fetch(:application)
35
+ end
36
+
37
+ # Assets path
38
+ #
39
+ # @return [String]
40
+ def assets_path
41
+ File.join shared_path, 'assets'
42
+ end
43
+
44
+ # File path to remote database config file
45
+ #
46
+ # @return [String]
47
+ def remote_database_config_path
48
+ File.join shared_path, 'config', "db_settings.#{fetch(:stage)}.yaml"
49
+ end
50
+
51
+ # File path to local database config file
52
+ #
53
+ # @return [String]
54
+ def local_database_config_path
55
+ File.join 'temp', "db_settings.#{fetch(:stage)}.yaml"
56
+ end
57
+
58
+ # Default timestamp format for database dump files
59
+ #
60
+ # @return [String]
61
+ def datetime_format
62
+ '%Y-%m-%d_%H-%M'
63
+ end
64
+
65
+ # List of table names to be ignored by default when dumping from database
66
+ #
67
+ # @return [String]
68
+ def default_ignore_tables
69
+ %w(
70
+ cache_extensions cache_hash cache_imagesizes cache_md5params
71
+ cache_pages cache_pagesection cache_sys_dmail_stat cache_treelist cache_typo3temp_log
72
+ cachingframework_cache_hash cachingframework_cache_hash_tags cachingframework_cache_pages
73
+ cachingframework_cache_pages_tags cachingframework_cache_pagesection
74
+ cachingframework_cache_pagesection_tags sys_workspace_cache sys_workspace_cache_tags
75
+ tt_news_cache tt_news_cache_tags tx_extbase_cache_object tx_extbase_cache_object_tags
76
+ tx_extbase_cache_reflection tx_extbase_cache_reflection_tags tx_realurl_chashcache
77
+ tx_realurl_pathcache tx_realurl_urldecodecache tx_realurl_urlencodecache be_users be_sessions
78
+ sys_domain fe_users fe_sessions fe_session_data
79
+ cf_cache_hash cf_cache_hash_tags cf_cache_pages cf_cache_pages_tags cf_cache_pagesection
80
+ cf_cache_pagesection_tags cf_cache_rootline cf_cache_rootline_tags cf_extbase_datamapfactory_datamap
81
+ cf_extbase_datamapfactory_datamap_tags cf_extbase_object cf_extbase_object_tags
82
+ cf_extbase_reflection cf_extbase_reflection_tags cf_extbase_typo3dbbackend_queries
83
+ cf_extbase_typo3dbbackend_queries_tags cf_extbase_typo3dbbackend_tablecolumns
84
+ cf_extbase_typo3dbbackend_tablecolumns_tags
85
+ )
86
+ end
87
+
88
+ # List of table names to be ignored when dumping from database defined via Capistrano variable or environment variable
89
+ #
90
+ # @return [String]
91
+ def additional_ignore_tables
92
+ env_string_list = ENV.fetch('ADDITIONAL_IGNORE_TABLES', '').split ' '
93
+ cap_string_list = fetch(:additional_ignore_tables, '').split ' '
94
+ env_string_list | cap_string_list
95
+ end
96
+
97
+ # List of table names to be ignored when dumping from database
98
+ #
99
+ # @return [String]
100
+ def ignore_tables
101
+ default_ignore_tables | additional_ignore_tables
102
+ end
103
+
104
+ #####################################################
105
+ # Local temporary directory constants
106
+ #####################################################
107
+
108
+ # Archive filename as singleton
109
+ # Note: if the archive filename doesn't already exist it will be generated
110
+ #
111
+ # @return [String]
112
+ def archive_filename
113
+ @archive_filename ||= Dir::Tmpname.make_tmpname [application + '_', '.tar.gz'], nil
114
+ end
115
+
116
+ # Local temporary directory path as singleton
117
+ # Note: if the directory doesn't already exist it will be created
118
+ #
119
+ # @return [String]
120
+ def local_tmp_dir
121
+ @local_tmp_dir ||= Dir.mktmpdir
122
+ end
123
+
124
+ # Archive path in a local temporary directory
125
+ #
126
+ # @return [String]
127
+ def local_exclude_path
128
+ File.join local_tmp_dir, 'exclude.txt'
129
+ end
130
+
131
+ # Archive path in a local temporary directory
132
+ #
133
+ # @return [String]
134
+ def local_archive_path
135
+ File.join local_tmp_dir, archive_filename
136
+ end
137
+
138
+ #####################################################
139
+ # remote paths constants
140
+ #####################################################
141
+
142
+ # Remote temporary directory path
143
+ #
144
+ # @return [String]
145
+ def remote_tmp_dir
146
+ File.join fetch(:tmp_dir), application
147
+ end
148
+
149
+ # Archive path in a remote temporary directory
150
+ #
151
+ # @return [String]
152
+ def remote_archive_path
153
+ File.join remote_tmp_dir, archive_filename
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,121 @@
1
+ require 'tmpdir'
2
+ require 'capistrano/scm'
3
+ require 'capistrano/i18n'
4
+ require 'dkdeploy/i18n'
5
+ require 'dkdeploy/constants'
6
+
7
+ include Capistrano::DSL
8
+
9
+ module Dkdeploy
10
+ # Class for the capistrano copy
11
+ #
12
+ class Copy
13
+ include Dkdeploy::Constants
14
+
15
+ # Provide a wrapper for the SCM that loads a strategy for the user.
16
+ #
17
+ # @param [Rake] context The context in which the strategy should run
18
+ # @param [Module] strategy A module to include into the SCM instance. The
19
+ # module should provide the abstract methods of Capistrano::SCM
20
+ #
21
+ def initialize(context, strategy)
22
+ @context = context
23
+ singleton = class << self; self; end
24
+ singleton.send(:include, strategy)
25
+ end
26
+
27
+ # Default copy strategy
28
+ #
29
+ module DefaultStrategy
30
+ # Checks if the local source directory exists.
31
+ #
32
+ def check
33
+ # scope object for the processing in the block
34
+ me = self
35
+ run_locally do
36
+ unless Dir.exist? me.copy_source
37
+ fatal I18n.t('directory.not_exists', name: me.copy_source, scope: :dkdeploy)
38
+ exit 1
39
+ end
40
+ end
41
+ end
42
+
43
+ # Copies the source file structure to the server excluding the not wanted elements.
44
+ #
45
+ def release
46
+ build_source_archive
47
+ copy_archive_to_server
48
+ clean_up_temporary_sources
49
+ end
50
+
51
+ # Builds a tar archive in a temporary directory
52
+ # excluding files and folders matching the configured pattern.
53
+ #
54
+ def build_source_archive
55
+ # scope object for the processing in the block
56
+ me = self
57
+ run_locally do
58
+ info I18n.t('tasks.copy.archive.generate', scope: :dkdeploy)
59
+
60
+ # generate an exclude.txt file with the patterns to be excluded
61
+ File.open(me.local_exclude_path, 'w+') do |file|
62
+ me.copy_exclude.each do |pattern|
63
+ file.puts pattern
64
+ end
65
+ end
66
+
67
+ # build the tar archive excluding the patterns from exclude.txt
68
+ within me.copy_source do
69
+ execute :tar, '-X ' + me.local_exclude_path, '-cpzf', me.local_archive_path, '.'
70
+ end
71
+ end
72
+ end
73
+
74
+ # Copies the tar archive on the remote server
75
+ # extracting it to the configured directory.
76
+ #
77
+ def copy_archive_to_server # rubocop:disable Metrics/AbcSize
78
+ # scope object for the processing in the block
79
+ me = self
80
+ on release_roles :app do
81
+ info I18n.t('file.upload', file: 'archive', target: me.remote_tmp_dir, scope: :dkdeploy)
82
+ execute :mkdir, '-p', me.remote_tmp_dir
83
+
84
+ upload! me.local_archive_path, me.remote_tmp_dir
85
+
86
+ info I18n.t('directory.create', directory: release_path, scope: :dkdeploy)
87
+ execute :mkdir, '-p', release_path
88
+
89
+ within release_path do
90
+ info I18n.t('tasks.copy.archive.extract', target: release_path, scope: :dkdeploy)
91
+ execute :tar, '-xpzf', me.remote_archive_path
92
+ end
93
+ end
94
+ end
95
+
96
+ # Cleans up the local and remote temporary directories
97
+ #
98
+ def clean_up_temporary_sources
99
+ # scope object for the processing in the block
100
+ me = self
101
+
102
+ # remove the local temporary directory
103
+ run_locally do
104
+ info I18n.t('file.remove', path: me.local_tmp_dir, scope: :dkdeploy)
105
+ execute :rm, '-rf', me.local_tmp_dir
106
+ end
107
+
108
+ # removes the remote archive
109
+ on release_roles :app do
110
+ info I18n.t('file.remove', path: me.remote_archive_path, scope: :dkdeploy)
111
+ execute :rm, '-f', me.remote_archive_path
112
+ end
113
+ end
114
+
115
+ # Fetches the current revision message
116
+ def fetch_revision
117
+ I18n.t('log.revision_log_message', copy_source: fetch(:copy_source), time: Time.now, scope: :dkdeploy)
118
+ end
119
+ end
120
+ end
121
+ end