Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions lib/overcommit/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,7 @@ def parent_command
return if Process.ppid.zero?

if OS.windows?
result = execute([
'powershell.exe', '-NoProfile', '-NonInteractive', '-Command',
"(Get-CimInstance Win32_Process -Filter 'ProcessId = #{Process.ppid}').CommandLine",
])
command = result.stdout.strip
command if result.success? && !command.empty?
windows_parent_command
elsif OS.cygwin?
# Cygwin's `ps` command behaves differently than the traditional
# Linux version, but a comparable `procps` is provided to compensate.
Expand Down Expand Up @@ -294,6 +289,29 @@ def matches_path?(pattern, path)

private

def windows_parent_command
native_windows_parent_command
rescue LoadError, StandardError
# win32ole is optional and absent from newer Ruby distributions.
# Fall back when it is unavailable or the COM query is denied.
result = execute([
'powershell.exe', '-NoProfile', '-NonInteractive', '-Command',
"(Get-CimInstance Win32_Process -Filter 'ProcessId = #{Process.ppid}').CommandLine",
])
command = result.stdout.strip
command if result.success? && !command.empty?
end

# Avoid starting PowerShell for every query when Ruby provides COM support.
def native_windows_parent_command
require 'win32ole'
service = WIN32OLE.connect('winmgmts:root/cimv2')
processes = service.ExecQuery(
"SELECT CommandLine FROM Win32_Process WHERE ProcessId = #{Process.ppid}"
)
processes.to_enum.first&.CommandLine
end

# Log debug output.
#
# This is necessary since some specs indirectly call utility functions but
Expand Down
59 changes: 59 additions & 0 deletions spec/overcommit/utils_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@
context 'when the Windows process query cannot be launched' do
before do
Overcommit::OS.stub(:windows?).and_return(true)
described_class.stub(:native_windows_parent_command).and_raise(LoadError)
described_class.stub(:execute).and_raise(ChildProcess::LaunchError, 'Access denied')
end

Expand All @@ -153,13 +154,71 @@
context 'when the Windows parent has already exited' do
before do
Overcommit::OS.stub(:windows?).and_return(true)
described_class.stub(:native_windows_parent_command).and_raise(LoadError)
described_class.stub(:execute).
and_return(Overcommit::Subprocess::Result.new(0, '', ''))
end

it { should be_nil }
end

context 'when Windows provides COM support' do
let(:service) { double('WMI service') }
let(:command) { 'git -C "C:/Users/José/项目" commit --amend' }
let(:processes) { [double('process', CommandLine: command)] }

before do
Overcommit::OS.stub(:windows?).and_return(true)
described_class.stub(:require).with('win32ole').and_return(true)
stub_const('WIN32OLE', double(connect: service))
service.stub(:ExecQuery).
with("SELECT CommandLine FROM Win32_Process WHERE ProcessId = #{Process.pid}").
and_return(processes)
end

it 'returns the complete Unicode command line without launching PowerShell' do
described_class.should_not_receive(:execute)
subject.should == command
end

context 'when the process has exited' do
let(:processes) { [] }

it { should be_nil }
end

context 'when the command line is inaccessible' do
let(:command) { nil }

it { should be_nil }
end

context 'when COM access is denied' do
before do
service.stub(:ExecQuery).and_raise(StandardError, 'Access denied')
described_class.stub(:execute).
and_return(Overcommit::Subprocess::Result.new(0, "git commit\n", ''))
end

it 'falls back to PowerShell' do
subject.should == 'git commit'
end
end
end

context 'when win32ole is not installed' do
before do
Overcommit::OS.stub(:windows?).and_return(true)
described_class.stub(:require).with('win32ole').and_raise(LoadError)
described_class.stub(:execute).
and_return(Overcommit::Subprocess::Result.new(0, "git commit\n", ''))
end

it 'falls back to PowerShell' do
subject.should == 'git commit'
end
end

context 'when there is no parent' do
before do
Process.stub(:ppid) { 0 }
Expand Down
Loading