• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Nu-hin / remote_ruby / 13615159898

02 Mar 2025 11:57AM UTC coverage: 92.648% (-0.09%) from 92.736%
13615159898

push

github

Nu-hin
Delete temp file in a separate SSH session for reliability

0 of 2 new or added lines in 1 file covered. (0.0%)

983 of 1061 relevant lines covered (92.65%)

83.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

17.91
/lib/remote_ruby/ssh_adapter.rb
1
# frozen_string_literal: true
2

3
require 'net/ssh'
20✔
4
require 'remote_ruby/pipes'
20✔
5

6
module RemoteRuby
20✔
7
  # An adapter for executing Ruby code on a remote host via SSH
8
  class SSHAdapter < ConnectionAdapter
20✔
9
    UnableToExecuteError = Class.new(StandardError)
20✔
10

11
    attr_reader :host, :config, :working_dir, :user
20✔
12

13
    def initialize(host:, working_dir: nil, use_ssh_config_file: true, **params)
20✔
14
      super
×
15
      @host = host
×
16
      @working_dir = working_dir
×
17
      @config = Net::SSH.configuration_for(@host, use_ssh_config_file)
×
18

19
      @config = @config.merge(params)
×
20
      @user = @config[:user]
×
21
    end
22

23
    def open(code)
20✔
24
      Net::SSH.start(host, nil, config) do |ssh|
×
25
        with_temp_file(code, ssh) do |fname|
×
26
          Pipes.with_pipes do |p|
×
27
            t = Thread.new do
×
28
              res = run_code(ssh, fname, p.in_r, p.out_w, p.err_w)
×
29
              get_result(ssh, fname, p.res_w, res)
×
30
            end
31

32
            yield p.in_w, p.out_r, p.err_r, p.res_r
×
33
            t.join
×
34
          end
35
        end
36
      end
37
    end
38

39
    def run_code(ssh, fname, stdin_r, stdout_w, stderr_w)
20✔
40
      res = nil
×
41
      ssh.open_channel do |channel|
×
42
        channel.exec("cd '#{working_dir}' && ruby \"#{fname}\"") do |ch, success|
×
43
          raise UnableToExecuteError unless success
×
44

45
          ssh.listen_to(stdin_r) do |io|
×
46
            data = io.read_nonblock(4096)
×
47
            ch.send_data(data)
×
48
          rescue EOFError
49
            ch.eof!
×
50
          end
51

52
          ch.on_data do |_, data|
×
53
            stdout_w << data
×
54
          end
55

56
          ch.on_extended_data do |_, _, data|
×
57
            stderr_w << data
×
58
          end
59

60
          ch.on_request('exit-status') do |_, data|
×
61
            res = data.read_long
×
62
          end
63

64
          ch.on_close do |_|
×
65
            stdout_w.close
×
66
            stderr_w.close
×
67
          end
68
        end
69
      end.wait
70
      res
×
71
    end
72

73
    def get_result(ssh, fname, res_w, process_status)
20✔
74
      unless process_status.zero?
×
75
        res_w.close
×
76
        raise "Process exited with code #{process_status}"
×
77
      end
78

79
      ssh.open_channel do |channel|
×
80
        channel.exec("cat \"#{fname}\"") do |ch, success|
×
81
          raise UnableToExecuteError unless success
×
82

83
          ch.on_data do |_, data|
×
84
            res_w << data
×
85
          end
86

87
          ch.on_close do |_|
×
88
            res_w.close
×
89
          end
90
        end
91
      end.wait
92
    end
93

94
    def connection_name
20✔
95
      "#{user}@#{host}:#{working_dir || '~'}> "
×
96
    end
97

98
    def with_temp_file(code, ssh)
20✔
99
      fname = String.new
×
100
      ssh.open_channel do |channel|
×
101
        channel.exec('f=$(mktemp --tmpdir remote_ruby.XXXXXX) && cat > $f && echo $f') do |ch, success|
×
102
          raise UnableToExecuteError unless success
×
103

104
          ch.on_data do |_, data|
×
105
            fname << data
×
106
          end
107

108
          ch.send_data(code)
×
109
          ch.eof!
×
110
        end
111
      end.wait
112

113
      fname.strip!
×
114

115
      yield fname
×
116
    ensure
NEW
117
      Net::SSH.start(host, nil, config) do |del_ssh|
×
NEW
118
        del_ssh.exec!("rm \"#{fname}\"")
×
119
      end
120
    end
121
  end
122
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc