pdk 1.14.1 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/lib/pdk/answer_file.rb +5 -7
  4. data/lib/pdk/cli.rb +1 -0
  5. data/lib/pdk/cli/console.rb +1 -1
  6. data/lib/pdk/cli/convert.rb +10 -2
  7. data/lib/pdk/cli/exec.rb +2 -1
  8. data/lib/pdk/cli/module/build.rb +1 -1
  9. data/lib/pdk/cli/module/generate.rb +1 -1
  10. data/lib/pdk/cli/release.rb +192 -0
  11. data/lib/pdk/cli/release/prep.rb +39 -0
  12. data/lib/pdk/cli/release/publish.rb +40 -0
  13. data/lib/pdk/cli/update.rb +12 -0
  14. data/lib/pdk/config.rb +1 -1
  15. data/lib/pdk/config/namespace.rb +1 -1
  16. data/lib/pdk/generate/module.rb +11 -17
  17. data/lib/pdk/generate/puppet_object.rb +1 -2
  18. data/lib/pdk/generate/task.rb +1 -1
  19. data/lib/pdk/module.rb +2 -1
  20. data/lib/pdk/module/build.rb +15 -25
  21. data/lib/pdk/module/convert.rb +4 -9
  22. data/lib/pdk/module/metadata.rb +1 -3
  23. data/lib/pdk/module/release.rb +260 -0
  24. data/lib/pdk/module/template_dir.rb +115 -0
  25. data/lib/pdk/module/template_dir/base.rb +268 -0
  26. data/lib/pdk/module/template_dir/git.rb +91 -0
  27. data/lib/pdk/module/template_dir/local.rb +21 -0
  28. data/lib/pdk/module/update.rb +17 -5
  29. data/lib/pdk/module/update_manager.rb +1 -1
  30. data/lib/pdk/report.rb +18 -12
  31. data/lib/pdk/report/event.rb +6 -3
  32. data/lib/pdk/template_file.rb +2 -2
  33. data/lib/pdk/util.rb +17 -6
  34. data/lib/pdk/util/bundler.rb +8 -9
  35. data/lib/pdk/util/changelog_generator.rb +115 -0
  36. data/lib/pdk/util/filesystem.rb +62 -2
  37. data/lib/pdk/util/git.rb +60 -8
  38. data/lib/pdk/util/puppet_version.rb +4 -5
  39. data/lib/pdk/util/ruby_version.rb +3 -3
  40. data/lib/pdk/util/template_uri.rb +49 -40
  41. data/lib/pdk/util/version.rb +4 -4
  42. data/lib/pdk/validate/metadata/metadata_syntax.rb +2 -2
  43. data/lib/pdk/validate/puppet/puppet_epp.rb +2 -4
  44. data/lib/pdk/validate/puppet/puppet_syntax.rb +2 -4
  45. data/lib/pdk/validate/tasks/metadata_lint.rb +2 -2
  46. data/lib/pdk/validate/yaml/syntax.rb +3 -3
  47. data/lib/pdk/version.rb +1 -1
  48. data/locales/pdk.pot +401 -149
  49. metadata +11 -3
  50. data/lib/pdk/module/templatedir.rb +0 -391
@@ -0,0 +1,21 @@
1
+ require 'pdk'
2
+ require 'pdk/module/template_dir/base'
3
+
4
+ module PDK
5
+ module Module
6
+ module TemplateDir
7
+ class Local < Base
8
+ def template_path(uri)
9
+ [uri.shell_path, false]
10
+ end
11
+
12
+ # For plain fileystem directories, this will return the URL to the repository only.
13
+ #
14
+ # @return [Hash{String => String}] A hash of identifying metadata.
15
+ def metadata
16
+ super.merge('template-url' => uri.bare_uri)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -6,7 +6,7 @@ module PDK
6
6
  GIT_DESCRIBE_PATTERN = %r{\A(?<base>.+?)-(?<additional_commits>\d+)-g(?<sha>.+)\Z}
7
7
 
8
8
  def run
9
- template_uri.git_ref = new_template_version
9
+ template_uri.uri_fragment = new_template_version
10
10
 
11
11
  stage_changes!
12
12
 
@@ -71,15 +71,27 @@ module PDK
71
71
  def new_template_version
72
72
  return options[:'template-ref'] if options[:'template-ref']
73
73
 
74
- if template_uri.default? && template_uri.ref_is_tag? && PDK::Util.package_install?
74
+ if template_uri.default? && PDK::Util::Git.tag?(template_uri.bare_uri, template_uri.uri_fragment) && PDK::Util.package_install?
75
75
  PDK::Util::TemplateURI.default_template_ref
76
76
  else
77
- template_uri.git_ref
77
+ template_uri.uri_fragment
78
78
  end
79
79
  end
80
80
 
81
+ def pinned_to_puppetlabs_template_tag?
82
+ return false unless template_uri.puppetlabs_template?
83
+ return false unless PDK::Util::Git.tag?(template_uri.bare_uri, template_uri.uri_fragment)
84
+ return false if latest_template?
85
+
86
+ template_uri.uri_fragment == new_template_version
87
+ end
88
+
81
89
  private
82
90
 
91
+ def latest_template?
92
+ [PDK::TEMPLATE_REF, 'master'].include?(template_uri.uri_fragment)
93
+ end
94
+
83
95
  def current_template_version
84
96
  @current_template_version ||= module_metadata.data['template-ref']
85
97
  end
@@ -101,7 +113,7 @@ module PDK
101
113
  return template_ref if template_ref == PDK::TEMPLATE_REF
102
114
 
103
115
  sha_length = GIT_DESCRIBE_PATTERN.match(current_template_version)[:sha].length - 1
104
- "#{template_ref}@#{PDK::Util::Git.ls_remote(template_uri.git_remote, template_ref)[0..sha_length]}"
116
+ "#{template_ref}@#{PDK::Util::Git.ls_remote(template_uri.bare_uri, template_ref)[0..sha_length]}"
105
117
  end
106
118
 
107
119
  def update_message
@@ -113,7 +125,7 @@ module PDK
113
125
 
114
126
  format_string % {
115
127
  module_name: module_metadata.data['name'],
116
- template_url: template_uri.git_remote,
128
+ template_url: template_uri.bare_uri,
117
129
  current_version: current_version,
118
130
  new_version: new_version,
119
131
  }
@@ -186,7 +186,7 @@ module PDK
186
186
 
187
187
  require 'diff/lcs/hunk'
188
188
 
189
- file_mtime = File.stat(path).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z')
189
+ file_mtime = PDK::Util::Filesystem.stat(path).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z')
190
190
  now = Time.now.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z')
191
191
 
192
192
  output << "--- #{path}\t#{file_mtime}"
@@ -51,9 +51,6 @@ module PDK
51
51
  require 'time'
52
52
  require 'socket'
53
53
 
54
- # Open a File Object for IO if target is a string containing a filename or path
55
- target = File.open(target, 'w') if target.is_a? String
56
-
57
54
  document = REXML::Document.new
58
55
  document << REXML::XMLDecl.new
59
56
  testsuites = REXML::Element.new('testsuites')
@@ -81,9 +78,14 @@ module PDK
81
78
  end
82
79
 
83
80
  document.elements << testsuites
84
- document.write(target, 2)
85
- ensure
86
- target.close if target.is_a? File
81
+ report = ''
82
+ document.write(report, 2)
83
+
84
+ if target.is_a?(String)
85
+ PDK::Util::Filesystem.write_file(target, report)
86
+ else
87
+ target << report
88
+ end
87
89
  end
88
90
 
89
91
  # Renders the report as plain text.
@@ -94,22 +96,26 @@ module PDK
94
96
  # @param target [#write] an IO object that the report will be written to.
95
97
  # Defaults to PDK::Report.default_target.
96
98
  def write_text(target = self.class.default_target)
97
- # Open a File Object for IO if target is a string containing a filename or path
98
- target = File.open(target, 'w') if target.is_a? String
99
99
  coverage_report = nil
100
+ report = []
100
101
 
101
102
  events.each do |_tool, tool_events|
102
103
  tool_events.each do |event|
103
104
  if event.rspec_puppet_coverage?
104
105
  coverage_report = event.to_text
105
106
  else
106
- target.puts(event.to_text) unless event.pass?
107
+ report << event.to_text unless event.pass?
107
108
  end
108
109
  end
109
110
  end
110
- ensure
111
- target.puts "\n#{coverage_report}" if coverage_report
112
- target.close if target.is_a? File
111
+
112
+ report << "\n#{coverage_report}" if coverage_report
113
+
114
+ if target.is_a?(String)
115
+ PDK::Util::Filesystem.write_file(target, report.join("\n"))
116
+ else
117
+ target << report.join("\n")
118
+ end
113
119
  end
114
120
  end
115
121
  end
@@ -98,7 +98,7 @@ module PDK
98
98
  # results.
99
99
  def rspec_puppet_coverage?
100
100
  @rspec_puppet_coverage_pattern ||= File.join('**', 'lib', 'rspec-puppet', 'coverage.rb')
101
- source == 'rspec' && File.fnmatch?(@rspec_puppet_coverage_pattern, File.expand_path(file))
101
+ source == 'rspec' && PDK::Util::Filesystem.fnmatch?(@rspec_puppet_coverage_pattern, PDK::Util::Filesystem.expand_path(file))
102
102
  end
103
103
 
104
104
  # Renders the event in a clang style text format.
@@ -346,10 +346,13 @@ module PDK
346
346
  def context_lines(max_num_lines = 5)
347
347
  return if file.nil? || line.nil?
348
348
 
349
- file_path = [file, File.join(PDK::Util.module_root, file)].find { |r| File.file?(r) }
349
+ file_path = [file, File.join(PDK::Util.module_root, file)].find do |path|
350
+ PDK::Util::Filesystem.file?(path)
351
+ end
352
+
350
353
  return if file_path.nil?
351
354
 
352
- file_content = File.read(file_path).split("\n")
355
+ file_content = PDK::Util::Filesystem.read_file(file_path).split("\n")
353
356
  delta = (max_num_lines - 1) / 2
354
357
  min = [0, (line - 1) - delta].max
355
358
  max = [(line - 1) + delta, file_content.length].min
@@ -62,8 +62,8 @@ module PDK
62
62
  #
63
63
  # @api private
64
64
  def template_content
65
- if File.file?(@template_file) && File.readable?(@template_file)
66
- return File.read(@template_file)
65
+ if PDK::Util::Filesystem.file?(@template_file) && PDK::Util::Filesystem.readable?(@template_file)
66
+ return PDK::Util::Filesystem.read_file(@template_file)
67
67
  end
68
68
 
69
69
  raise ArgumentError, _("'%{template}' is not a readable file") % { template: @template_file }
@@ -10,6 +10,7 @@ autoload :Pathname, 'pathname'
10
10
  module PDK
11
11
  module Util
12
12
  autoload :Bundler, 'pdk/util/bundler'
13
+ autoload :ChangelogGenerator, 'pdk/util/changelog_generator'
13
14
  autoload :Env, 'pdk/util/env'
14
15
  autoload :Filesystem, 'pdk/util/filesystem'
15
16
  autoload :Git, 'pdk/util/git'
@@ -31,6 +32,16 @@ module PDK
31
32
  types
32
33
  ].freeze
33
34
 
35
+ #:nocov:
36
+ # This method just wraps core Ruby functionality and
37
+ # can be ignored for code coverage
38
+
39
+ # Calls Kernel.exit with an exitcode
40
+ def exit_process(exit_code)
41
+ exit exit_code
42
+ end
43
+ #:nocov:
44
+
34
45
  # Searches upwards from current working directory for the given target file.
35
46
  #
36
47
  # @param target [String] Name of file to search for.
@@ -40,13 +51,13 @@ module PDK
40
51
  # nil if the target file could not be found.
41
52
  def find_upwards(target, start_dir = nil)
42
53
  previous = nil
43
- current = File.expand_path(start_dir || Dir.pwd)
54
+ current = PDK::Util::Filesystem.expand_path(start_dir || Dir.pwd)
44
55
 
45
- until !File.directory?(current) || current == previous
56
+ until !PDK::Util::Filesystem.directory?(current) || current == previous
46
57
  filename = File.join(current, target)
47
- return filename if File.file?(filename)
58
+ return filename if PDK::Util::Filesystem.file?(filename)
48
59
  previous = current
49
- current = File.expand_path('..', current)
60
+ current = PDK::Util::Filesystem.expand_path('..', current)
50
61
  end
51
62
  end
52
63
  module_function :find_upwards
@@ -72,12 +83,12 @@ module PDK
72
83
  # @return [String] Canonical path
73
84
  def canonical_path(path)
74
85
  if Gem.win_platform?
75
- unless File.exist?(path)
86
+ unless PDK::Util::Filesystem.exist?(path)
76
87
  raise PDK::CLI::FatalError, _("Cannot resolve a full path to '%{path}', as it does not currently exist.") % { path: path }
77
88
  end
78
89
  PDK::Util::Windows::File.get_long_pathname(path)
79
90
  else
80
- File.expand_path(path)
91
+ PDK::Util::Filesystem.expand_path(path)
81
92
  end
82
93
  end
83
94
  module_function :canonical_path
@@ -6,8 +6,6 @@ module PDK
6
6
  class BundleHelper; end
7
7
 
8
8
  def self.ensure_bundle!(gem_overrides = nil)
9
- require 'fileutils'
10
-
11
9
  bundle = BundleHelper.new
12
10
 
13
11
  # This will default ensure_bundle! to re-resolving everything to latest
@@ -34,11 +32,11 @@ module PDK
34
32
  original_lockfile = bundle.gemfile_lock
35
33
  temp_lockfile = "#{original_lockfile}.tmp"
36
34
 
37
- FileUtils.mv(original_lockfile, temp_lockfile)
35
+ PDK::Util::Filesystem.mv(original_lockfile, temp_lockfile)
38
36
 
39
37
  all_deps_available = bundle.installed?(gem_overrides)
40
38
  ensure
41
- FileUtils.mv(temp_lockfile, original_lockfile, force: true)
39
+ PDK::Util::Filesystem.mv(temp_lockfile, original_lockfile, force: true)
42
40
  end
43
41
 
44
42
  bundle.update_lock!(with: gem_overrides, local: all_deps_available)
@@ -91,7 +89,7 @@ module PDK
91
89
  end
92
90
 
93
91
  def locked?
94
- !gemfile_lock.nil? && File.file?(gemfile_lock)
92
+ !gemfile_lock.nil? && PDK::Util::Filesystem.file?(gemfile_lock)
95
93
  end
96
94
 
97
95
  def installed?(gem_overrides = {})
@@ -110,7 +108,6 @@ module PDK
110
108
 
111
109
  def lock!
112
110
  require 'pdk/util'
113
- require 'fileutils'
114
111
  require 'pdk/util/ruby_version'
115
112
 
116
113
  if PDK::Util.package_install?
@@ -121,7 +118,9 @@ module PDK
121
118
  File.join(PDK::Util.package_cachedir, 'Gemfile.lock'),
122
119
  ]
123
120
 
124
- vendored_gemfile_lock = vendored_lockfiles.find { |lockfile| File.exist?(lockfile) }
121
+ vendored_gemfile_lock = vendored_lockfiles.find do |lockfile|
122
+ PDK::Util::Filesystem.exist?(lockfile)
123
+ end
125
124
 
126
125
  unless vendored_gemfile_lock
127
126
  raise PDK::CLI::FatalError, _('Vendored Gemfile.lock (%{source}) not found.') % {
@@ -130,7 +129,7 @@ module PDK
130
129
  end
131
130
 
132
131
  PDK.logger.debug(_('Using vendored Gemfile.lock from %{source}.') % { source: vendored_gemfile_lock })
133
- FileUtils.cp(vendored_gemfile_lock, File.join(PDK::Util.module_root, 'Gemfile.lock'))
132
+ PDK::Util::Filesystem.cp(vendored_gemfile_lock, File.join(PDK::Util.module_root, 'Gemfile.lock'))
134
133
  else
135
134
  argv = ['lock']
136
135
 
@@ -212,7 +211,7 @@ module PDK
212
211
 
213
212
  def binstubs!(gems)
214
213
  binstub_dir = File.join(File.dirname(gemfile), 'bin')
215
- return true if gems.all? { |gem| File.file?(File.join(binstub_dir, gem)) }
214
+ return true if gems.all? { |gem| PDK::Util::Filesystem.file?(File.join(binstub_dir, gem)) }
216
215
 
217
216
  cmd = bundle_command('binstubs', *gems, '--force')
218
217
  result = cmd.execute!
@@ -0,0 +1,115 @@
1
+ require 'pdk'
2
+
3
+ module PDK
4
+ module Util
5
+ module ChangelogGenerator
6
+ # Taken from the version regex in https://forgeapi.puppet.com/schemas/module.json
7
+ VERSION_REGEX = %r{^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$}
8
+
9
+ # Raises if the github_changelog_generator is not available
10
+ def self.github_changelog_generator_available!
11
+ require 'bundler'
12
+ raise PDK::CLI::ExitWithError, _('Unable to generate the changelog as the github_changelog_generator gem is not installed') unless Bundler.rubygems.find_name('github_changelog_generator').any?
13
+ end
14
+
15
+ # Runs the Changelog Generator gem (in the module's context) to automatically create a CHANGLELOG.MD file
16
+ #
17
+ # @returns [String] The content of the new Changelog
18
+ def self.generate_changelog
19
+ github_changelog_generator_available!
20
+
21
+ changelog_command = PDK::CLI::Exec::InteractiveCommand.new(PDK::CLI::Exec.bundle_bin, 'exec', 'rake', 'changelog')
22
+ changelog_command.context = :module
23
+
24
+ result = changelog_command.execute!
25
+ raise PDK::CLI::ExitWithError, _('Error generating changelog: %{stdout}' % { stdout: result[:stdout] }) unless result[:exit_code].zero?
26
+
27
+ output = changelog_content
28
+
29
+ raise PDK::CLI::ExitWithError, _('The generated changelog contains uncategorized Pull Requests. Please label them and try again. See %{changelog_file} for more details' % { changelog_file: changelog_file }) if output =~ %r{UNCATEGORIZED PRS; GO LABEL THEM} # rubocop:disable Metrics/LineLength
30
+ output
31
+ end
32
+
33
+ # Computes the next version, based on the content of a changelog
34
+ #
35
+ # @param current_version [String, Gem::Version] The current version of the module
36
+ # @return [String] The new version. May be the same as the current version if there are no notable changes
37
+ def self.compute_next_version(current_version)
38
+ raise PDK::CLI::ExitWithError, _('Invalid version string %{version}' % { version: current_version }) unless current_version =~ VERSION_REGEX
39
+ version = Gem::Version.create(current_version).segments
40
+ PDK.logger.info _('Determing the target version from \'%{file}\'') % { file: changelog_file }
41
+
42
+ # Grab all lines that start with ## between from the latest changes
43
+ # For example given the changelog below
44
+
45
+ # ```
46
+ # # Change log
47
+ #
48
+ # All notable changes to this project will be documented in this file.
49
+ #
50
+ # ## [v4.0.0](https://github.com/puppetlabs/puppetlabs-inifile/tree/v4.
51
+ #
52
+ # [Full Changelog](https://github.com/puppetlabs/puppetlabs-inifile/com --+
53
+ # |
54
+ # ### Changed |
55
+ # |
56
+ # - pdksync - FM-8499 - remove ubuntu14 support [\#363](https://github. | It's this piece of text we are interested in
57
+ # |
58
+ # ### Added |
59
+ # |
60
+ # - FM-8402 add debian 10 support [\#352](https://github.com/puppetlabs |
61
+ # |
62
+ # ## [v3.1.0](https://github.com/puppetlabs/puppetlabs-inifile/tree/v3. |
63
+ # --+
64
+ # [Full Changelog](https://github.com/puppetlabs/puppetlabs-inifile/com
65
+ #
66
+ # ### Added
67
+ #
68
+ # - FM-8222 - Port Module inifile to Litmus [\#344](https://github.com/
69
+ # - \(FM-8154\) Add Windows Server 2019 support [\#340](https://github.
70
+ # - \(FM-8041\) Add RedHat 8 support [\#339](https://github.com/puppetl
71
+ # ````
72
+ data = ''
73
+ in_changelog_entry = false
74
+ changelog_content.each_line do |line|
75
+ line.strip!
76
+ if line.start_with?('[')
77
+ # We're leaving the latest changes so we can break
78
+ break if in_changelog_entry
79
+ in_changelog_entry = true
80
+ end
81
+ if in_changelog_entry && line.start_with?('##')
82
+ data += line
83
+ end
84
+ end
85
+
86
+ # Check for meta headers in first two header line matches
87
+ if data =~ %r{^### Changed}
88
+ # Major Version bump
89
+ version[0] += 1
90
+ version[1] = 0
91
+ version[2] = 0
92
+ elsif data =~ %r{^### Added}
93
+ # Minor Version bump
94
+ version[1] += 1
95
+ version[2] = 0
96
+ elsif data =~ %r{^### Fixed}
97
+ # Patch Version bump
98
+ version[2] += 1
99
+ end
100
+
101
+ version.join('.')
102
+ end
103
+
104
+ def self.changelog_file
105
+ # Default Changelog file is CHANGELOG.md, but also search for the .MD prefix as well.
106
+ @changelog_file ||= ['CHANGELOG.md', 'CHANGELOG.MD'].map { |file| PDK::Util::Filesystem.expand_path(file) }.find { |path| PDK::Util::Filesystem.file?(path) }
107
+ end
108
+
109
+ def self.changelog_content
110
+ return '' if changelog_file.nil?
111
+ PDK::Util::Filesystem.read_file(changelog_file, open_args: 'rb:utf-8')
112
+ end
113
+ end
114
+ end
115
+ end
@@ -1,4 +1,5 @@
1
1
  require 'pdk'
2
+ autoload :FileUtils, 'fileutils'
2
3
 
3
4
  module PDK
4
5
  module Util
@@ -17,8 +18,8 @@ module PDK
17
18
  end
18
19
  module_function :write_file
19
20
 
20
- def read_file(file, nil_on_error: false)
21
- File.read(file)
21
+ def read_file(file, nil_on_error: false, open_args: 'r')
22
+ File.read(file, open_args: Array(open_args))
22
23
  rescue => e
23
24
  raise e unless nil_on_error
24
25
  nil
@@ -58,6 +59,11 @@ module PDK
58
59
  end
59
60
  module_function :fnmatch
60
61
 
62
+ def fnmatch?(*args)
63
+ File.fnmatch?(*args)
64
+ end
65
+ module_function :fnmatch?
66
+
61
67
  def readable?(*args)
62
68
  File.readable?(*args)
63
69
  end
@@ -72,6 +78,60 @@ module PDK
72
78
  FileUtils.rm(*args)
73
79
  end
74
80
  module_function :rm
81
+
82
+ def rm_f(*args)
83
+ FileUtils.rm_f(*args)
84
+ end
85
+ module_function :rm_f
86
+
87
+ def rm_rf(*args)
88
+ FileUtils.rm_rf(*args)
89
+ end
90
+ module_function :rm_rf
91
+
92
+ def remove_entry_secure(*args)
93
+ FileUtils.remove_entry_secure(*args)
94
+ end
95
+ module_function :remove_entry_secure
96
+
97
+ def zero?(*args)
98
+ File.zero?(*args)
99
+ end
100
+ module_function :zero?
101
+
102
+ def stat(*args)
103
+ File.stat(*args)
104
+ end
105
+ module_function :stat
106
+
107
+ def symlink?(*args)
108
+ File.symlink?(*args)
109
+ end
110
+ module_function :symlink?
111
+
112
+ def cp(*args)
113
+ FileUtils.cp(*args)
114
+ end
115
+ module_function :cp
116
+
117
+ def mv(*args)
118
+ FileUtils.mv(*args)
119
+ rescue Errno::ENOENT
120
+ # PDK-1169 - FileUtils.mv raises Errno::ENOENT when moving files inside
121
+ # VMWare shared folders on Windows. So we need to catch this
122
+ # case, check if the file exists to see if the exception is
123
+ # legit and "move" the file with cp & rm.
124
+ src, dest, opts = args
125
+ raise unless File.exist?(src)
126
+
127
+ FileUtils.cp(src, dest, preserve: true)
128
+ if (opts ||= {})[:secure]
129
+ FileUtils.remove_entry_secure(src, opts[:force])
130
+ else
131
+ FileUtils.remove_entry(src, opts[:force])
132
+ end
133
+ end
134
+ module_function :mv
75
135
  #:nocov:
76
136
  end
77
137
  end