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

Nu-hin / remote_ruby / 19941764447

04 Dec 2025 07:43PM UTC coverage: 95.253% (-0.06%) from 95.316%
19941764447

push

github

Nu-hin
Add code encryption

55 of 59 new or added lines in 7 files covered. (93.22%)

1 existing line in 1 file now uncovered.

1204 of 1264 relevant lines covered (95.25%)

160.55 hits per line

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

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

3
require 'net/ssh'
20✔
4
require 'remote_ruby/compat_io_reader'
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, :ruby_executable, :encryption_key_base64
20✔
12

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

22
      @config = @config.merge(params)
×
23
      @user = @config[:user]
×
24
    end
25

26
    def open(code, stdin, stdout, stderr)
20✔
27
      ret = nil
×
28
      Net::SSH.start(host, nil, config) do |ssh|
×
29
        with_temp_file(code, ssh) do |fname|
×
30
          res = run_code(ssh, fname, stdin, stdout, stderr)
×
31
          raise "Process exited with code #{res}" unless res.zero?
×
32

33
          ret = get_result(ssh, fname)
×
34
        end
35
      end
36
      ret
×
37
    end
38

39
    def connection_name
20✔
40
      "#{user}@#{host}:#{working_dir || '~'}> "
×
41
    end
42

43
    private
20✔
44

45
    def handle_stdin(chan, stdin)
20✔
46
      return if stdin.nil?
×
47

48
      if stdin.is_a?(StringIO)
×
49
        chan.send_data(stdin.string)
×
50
        chan.eof!
×
51
        return
×
52
      end
53

54
      stdin = RemoteRuby::CompatIOReader.new(stdin)
×
55

56
      chan.connection.listen_to(stdin.readable) do |io|
×
57
        data = io.read_nonblock(4096)
×
58
        chan.send_data(data)
×
59
      rescue EOFError
60
        chan.connection.stop_listening_to(stdin.readable)
×
61
        chan.eof!
×
62
      end
63

64
      chan.on_close do
×
65
        chan.connection.stop_listening_to(stdin.readable)
×
66
        stdin.join
×
67
      end
68
    end
69

70
    def handle_stdout(chan, stdout)
20✔
71
      return if stdout.nil?
×
72

73
      chan.on_data do |_, data|
×
74
        stdout.write(data)
×
75
      end
76
    end
77

78
    def handle_stderr(chan, stderr)
20✔
79
      return if stderr.nil?
×
80

81
      chan.on_extended_data do |_, _, data|
×
82
        stderr.write(data)
×
83
      end
84
    end
85

86
    def handle_exit_code(chan)
20✔
87
      chan.on_request('exit-status') do |_, data|
×
88
        yield data.read_long
×
89
      end
90
    end
91

92
    def run_remote_process(ssh, cmd, stdin, stdout, stderr)
20✔
93
      res = nil
×
94

95
      ssh.open_channel do |channel|
×
96
        channel.exec(cmd) do |ch, success|
×
97
          raise UnableToExecuteError unless success
×
98

99
          handle_stdin(ch, stdin)
×
100
          handle_stdout(ch, stdout)
×
101
          handle_stderr(ch, stderr)
×
102
          handle_exit_code(ch) do |code|
×
103
            res = code
×
104
          end
105
        end
106
      end.wait
107

108
      res
×
109
    end
110

111
    def run_code(ssh, fname, stdin, stdout, stderr)
20✔
NEW
112
      cmd = if encryption_key_base64.nil?
×
NEW
113
              "cd '#{working_dir}' && #{ruby_executable} \"#{fname}\""
×
114
            else
NEW
115
              "cd '#{working_dir}' && #{ruby_executable} \"#{fname}\" \"#{encryption_key_base64}\""
×
116
            end
UNCOV
117
      run_remote_process(ssh, cmd, stdin, stdout, stderr)
×
118
    end
119

120
    def get_result(ssh, fname)
20✔
121
      ssh.exec!("cat \"#{fname}\"")
×
122
    end
123

124
    def with_temp_file(code, ssh)
20✔
125
      out = StringIO.new
×
126
      cmd = 'f=$(mktemp --tmpdir remote_ruby.XXXXXX) && cat > $f && echo $f'
×
127
      run_remote_process(ssh, cmd, StringIO.new(code), out, nil)
×
128
      fname = out.string.strip
×
129

130
      yield fname
×
131
    ensure
132
      ssh.exec!("rm \"#{fname}\"")
×
133
    end
134
  end
135
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