Skip to content

Commit

Permalink
Properly close PTY parameters to get rid of leaks that cause Pty to f…
Browse files Browse the repository at this point in the history
…ail with 'Can't get Master/Slave device' error
  • Loading branch information
lacostej committed Feb 28, 2024
1 parent 3d9b9c2 commit df3ebda
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 2 deletions.
9 changes: 9 additions & 0 deletions fastlane_core/lib/fastlane_core/fastlane_pty.rb
Expand Up @@ -36,7 +36,16 @@ def self.spawn_with_pty(command, &block)
# https://stackoverflow.com/questions/10238298/ruby-on-linux-pty-goes-away-without-eof-raises-errnoeio
# This is expected on some linux systems, that indicates that the subcommand finished
# and we kept trying to read, ignore it
rescue => ex
failed = true
raise
ensure
# When an exception is thrown from the block command, we let the ecosystem a bit of time to complete
# nicely, flush buffers and terminate processes so that we don't abruptly stop the process
# and the process status ends in -1. This is far from a perfect solution.
sleep 0.05 if failed
command_stdin.close
command_stdout.close
begin
Process.wait(pid)
rescue Errno::ECHILD, PTY::ChildExited
Expand Down
14 changes: 12 additions & 2 deletions fastlane_core/spec/command_executor_spec.rb
Expand Up @@ -17,6 +17,11 @@
]
expect(fake_std_in).to receive(:each).and_yield(*fake_std_in).and_raise(Errno::EIO)

fake_std_out = 'not_really_std_out'

expect(fake_std_in).to receive(:close)
expect(fake_std_out).to receive(:close)

# Make a fake child process so we have a valid PID and $? is set correctly
expect(PTY).to receive(:spawn) do |command, &block|
expect(command).to eq('ls')
Expand All @@ -27,7 +32,7 @@
child_process_id = Process.spawn('echo foo', out: File::NULL)
expect(Process).to receive(:wait).with(child_process_id)

block.yield(fake_std_in, 'not_really_std_out', child_process_id)
block.yield(fake_std_in, fake_std_out, child_process_id)
end

result = FastlaneCore::CommandExecutor.execute(command: 'ls')
Expand All @@ -45,6 +50,11 @@
" - Muffins\n"
]

fake_std_out = 'not_really_std_out'

expect(fake_std_in).to receive(:close)
expect(fake_std_out).to receive(:close)

expect(PTY).to receive(:spawn) do |command, &block|
expect(command).to eq('echo foo')

Expand All @@ -54,7 +64,7 @@
child_process_id = Process.spawn('echo foo', out: File::NULL)
expect(Process).to receive(:wait).with(child_process_id)

block.yield(fake_std_in, 'not_really_std_out', child_process_id)
block.yield(fake_std_in, fake_std_out, child_process_id)
end

result = FastlaneCore::CommandExecutor.execute(command: 'echo foo')
Expand Down

0 comments on commit df3ebda

Please sign in to comment.