From 5967a23fa097e89726d335dcf781ae43cb256bc1 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Wed, 28 Apr 2021 12:44:06 -0700 Subject: [PATCH] Support pwsh executable name for powershell This updates the powershell detection to look for the `pwsh` executable and use it when the powershell executable is not available. --- lib/vagrant/util/powershell.rb | 44 +++++++++++------ test/unit/vagrant/util/powershell_test.rb | 57 ++++++++++++----------- 2 files changed, 60 insertions(+), 41 deletions(-) diff --git a/lib/vagrant/util/powershell.rb b/lib/vagrant/util/powershell.rb index 11a530ccf..2c24f1a35 100644 --- a/lib/vagrant/util/powershell.rb +++ b/lib/vagrant/util/powershell.rb @@ -14,28 +14,44 @@ module Vagrant MINIMUM_REQUIRED_VERSION = 3 # Number of seconds to wait while attempting to get powershell version DEFAULT_VERSION_DETECTION_TIMEOUT = 30 + # Names of the powershell executable + POWERSHELL_NAMES = ["powershell", "pwsh"].map(&:freeze).freeze + # Paths to powershell executable + POWERSHELL_PATHS = [ + "%WINDIR%/WindowsPowerShell/v1.0", + "%PROGRAMFILES%/PowerShell/7", + "%PROGRAMFILES%/PowerShell/6" + ].map(&:freeze).freeze + LOGGER = Log4r::Logger.new("vagrant::util::powershell") # @return [String|nil] a powershell executable, depending on environment def self.executable if !defined?(@_powershell_executable) - @_powershell_executable = "powershell" + # First start with detecting executable on configured path + POWERSHELL_NAMES.detect do |psh| + return @_powershell_executable = psh if Which.which(psh) + psh += ".exe" + return @_powershell_executable = psh if Which.which(psh) + end - if Which.which(@_powershell_executable).nil? - # Try to use WSL interoperability if PowerShell is not symlinked to - # the container. - if Platform.wsl? - @_powershell_executable += ".exe" + # Now attempt with paths + paths = POWERSHELL_PATHS.map do |ppath| + result = Util::Subprocess.execute("cmd.exe", "/c", "echo #{ppath}") + result.stdout.gsub("\"", "").strip if result.exit_code == 0 + end.compact - if Which.which(@_powershell_executable).nil? - @_powershell_executable = "/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe" + paths.each do |psh_path| + POWERSHELL_NAMES.each do |psh| + path = File.join(psh_path, psh) + return @_powershell_executable = path if Which.which(path) - if Which.which(@_powershell_executable).nil? - @_powershell_executable = nil - end - end - else - @_powershell_executable = nil + path += ".exe" + return @_powershell_executable = path if Which.which(path) + + # Finally test the msys2 style path + path = path.sub(/^([A-Za-z]):/, "/mnt/\\1") + return @_powershell_executable = path if Which.which(path) end end end diff --git a/test/unit/vagrant/util/powershell_test.rb b/test/unit/vagrant/util/powershell_test.rb index 62a2ab261..0b1123c0f 100644 --- a/test/unit/vagrant/util/powershell_test.rb +++ b/test/unit/vagrant/util/powershell_test.rb @@ -45,47 +45,50 @@ describe Vagrant::Util::PowerShell do end describe ".executable" do - before{ allow(Vagrant::Util::Platform).to receive(:wsl?).and_return(false) } + before do + allow(Vagrant::Util::Which).to receive(:which).and_return(nil) + allow(Vagrant::Util::Subprocess).to receive(:execute) do |*args| + Vagrant::Util::Subprocess::Result.new(0, args.last.sub("echo ", ""), "") + end + end - context "when found in PATH" do - before{ expect(Vagrant::Util::Which).to receive(:which).with("powershell").and_return(true) } + context "when powershell found in PATH" do + before{ expect(Vagrant::Util::Which).to receive(:which). + with("powershell").and_return(true) } it "should return powershell string" do expect(described_class.executable).to eq("powershell") end end - context "when not found in PATH" do - before{ expect(Vagrant::Util::Which).to receive(:which).with("powershell").and_return(nil) } + context "when pwsh found in PATH" do + before { expect(Vagrant::Util::Which).to receive(:which). + with("pwsh").and_return(true) } + it "should return pwsh string" do + expect(described_class.executable).to eq("pwsh") + end + end + + context "when not found in PATH" do it "should return nil" do expect(described_class.executable).to be_nil end - context "when within WSL" do - before do - allow(Vagrant::Util::Which).to receive(:which).with(/powershell/).and_return(nil) - expect(Vagrant::Util::Platform).to receive(:wsl?).and_return(true) - end + it "should check PATH with .exe extension" do + expect(Vagrant::Util::Which).to receive(:which).with("powershell.exe") + described_class.executable + end - it "should check PATH with .exe extension" do - expect(Vagrant::Util::Which).to receive(:which).with("powershell.exe") - described_class.executable - end + it "should return powershell.exe when found" do + expect(Vagrant::Util::Which).to receive(:which). + with("powershell.exe").and_return(true) + expect(described_class.executable).to eq("powershell.exe") + end - it "should return powershell.exe when found" do - expect(Vagrant::Util::Which).to receive(:which).with("powershell.exe").and_return(true) - expect(described_class.executable).to eq("powershell.exe") - end - - it "should return nil when not found" do - expect(described_class.executable).to be_nil - end - - it "should check for powershell with full path" do - expect(Vagrant::Util::Which).to receive(:which).with(/Windows\/System32.+powershell.exe/) - described_class.executable - end + it "should check for powershell with full path" do + expect(Vagrant::Util::Which).to receive(:which).with(/WindowsPowerShell\/v1.0\/powershell.exe/) + described_class.executable end end end