ruby-cli-daemon 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f0a57291fb717787ecf32c800114d81c9935386917e3e49c7515df597e83296f
4
+ data.tar.gz: b9495d51c4ad1311dc183efde2d78ef3b7c9156c4f57fb49e191539fa3defb80
5
+ SHA512:
6
+ metadata.gz: b4246d548789712085fa31fc1ec2452776fd36aa02d9b9b458e294db1677c9cae662f575b1b0b4620622c3ca0fbccb2b02c163087da186da1cd5b18d34f8a310
7
+ data.tar.gz: 1f55ac0797a42592da5e3b92b2dc98e8a6ebb70d5dbc5f47ec007581361b6c218715862ea4a3d7a6f4f5b660218f1f87c146a254392d13255bc04fabc1e2b9d8
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (C) 2013 Michael Grosser <michael@grosser.it>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env sh
2
+ set -e # add "x" to debug
3
+
4
+ lib=$(dirname $(realpath $0))/../lib
5
+
6
+ case "$1" in
7
+ stop)
8
+ # Not ruby-cli-daemon, so it does not kill my current editor in that folder
9
+ exec pkill -f ruby_cli_daemon
10
+ ;;
11
+ -v|--version)
12
+ exec ruby -r$lib/ruby_cli_daemon/version.rb -e "puts RubyCliDaemon::VERSION"
13
+ ;;
14
+ -h|--help)
15
+ echo "Usage:"
16
+ echo " ruby-cli-daemon <ruby-executable> [arg]*"
17
+ echo " Start or use background worker to execute command"
18
+ echo " For example: ruby-cli-daemon rake --version"
19
+ echo ""
20
+ echo " ruby-cli-daemon stop"
21
+ echo " Kill all spawned processes"
22
+ echo ""
23
+ echo "Options:"
24
+ echo " -v / --version Show version"
25
+ echo " -h / --help Show this help"
26
+ exit
27
+ ;;
28
+ esac
29
+
30
+ executable=$1
31
+ shift
32
+
33
+ # matching the `stop` pattern so everything can be killed quickly
34
+ socket=${TMPDIR}ruby_cli_daemon/$(basename $PWD)/${executable}
35
+ log=${TMPDIR}ruby_cli_daemon.log
36
+
37
+ # spawn new daemon if none exists
38
+ if [[ ! -e $socket ]]; then
39
+ # absolute executable so a single gem install is enough for all rubies
40
+ nohup ruby -r$lib/ruby_cli_daemon.rb -rbundler/setup -e RubyCliDaemon.start\ \"$socket\",\ \"$executable\" 0<&- &>$log &
41
+ while [ ! -e $socket ]; do
42
+ sleep 0.1
43
+ kill -0 $(jobs -p) || (echo "Failed to start worker, check $log" && false) # fail fast when worker failed
44
+ done
45
+ fi
46
+
47
+ # prepare output so we can start tailing
48
+ status="${socket}.status"
49
+ stdout="${socket}.out"
50
+ stderr="${socket}.err"
51
+ rm -f $status $stdout $stderr # clear previous
52
+ touch $stdout $stderr
53
+
54
+ # send the command to the daemon
55
+ echo $@ | nc -U $socket
56
+
57
+ # stream output
58
+ tail -f $stdout &
59
+ tail -f $stderr >&2 &
60
+
61
+ # wait for command to finish, tight loop so we don't lose time
62
+ while [ ! -f $status ]; do sleep 0.02; done
63
+
64
+ # kill log streamers, they should be done (but not the spawned worker)
65
+ for job in `jobs -p | tail -n2`; do kill $job; done
66
+
67
+ # replay exit status
68
+ exit "$(cat $status)"
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+ require "fileutils"
3
+ require "socket"
4
+ require "shellwords"
5
+
6
+ module RubyCliDaemon
7
+ TIMEOUT = 60 * 60
8
+
9
+ class << self
10
+ def install(path)
11
+ File.unlink(path) if File.exist?(path)
12
+ File.symlink(File.expand_path("../bin/ruby-sli-daemon.sh", __dir__), path)
13
+ FileUtils.chmod("+x", path)
14
+ end
15
+
16
+ def start(socket, executable)
17
+ server = create_socket(socket)
18
+ path = preload_gem(executable)
19
+
20
+ loop do
21
+ return unless (command = wait_for_command(server))
22
+
23
+ # execute the command in a fork
24
+ capture :STDOUT, "#{socket}.out" do
25
+ capture :STDERR, "#{socket}.err" do
26
+ _, status = Process.wait2(fork do
27
+ ARGV.replace(command)
28
+ load path
29
+ end)
30
+
31
+ # send back response
32
+ File.write("#{socket}.status", status.exitstatus)
33
+ end
34
+ end
35
+ end
36
+ ensure
37
+ # signal that this program is done so ruby-sli-daemon.sh restarts it
38
+ File.unlink socket if File.exist?(socket)
39
+ end
40
+
41
+ private
42
+
43
+ # preload the libraries we'll need to speed up execution
44
+ def preload_gem(executable)
45
+ require executable
46
+ path = Gem.bin_path(executable, executable)
47
+ GC.start # https://bugs.ruby-lang.org/issues/15878
48
+ path
49
+ end
50
+
51
+ def wait_for_command(server)
52
+ return unless IO.select([server], nil, nil, TIMEOUT)
53
+ connection = server.accept
54
+ command = connection.gets.shellsplit
55
+ connection.close
56
+ command
57
+ end
58
+
59
+ def create_socket(socket)
60
+ File.unlink socket if File.exist?(socket)
61
+ FileUtils.mkdir_p(File.dirname(socket))
62
+ UNIXServer.new(socket)
63
+ end
64
+
65
+ # StringIO does not work with rubies `system` call that `sh` uses under the hood, so using Tempfile + reopen
66
+ # https://grosser.it/2018/11/23/ruby-capture-stdout-without-stdout/
67
+ def capture(stream, path)
68
+ const = Object.const_get(stream)
69
+ old_stream = const.dup
70
+ const.flush # not sure if that's necessary
71
+ const.reopen(path)
72
+ yield
73
+ ensure
74
+ const.flush # not sure if that's necessary
75
+ const.reopen(old_stream)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module RubyCliDaemon
3
+ VERSION = "0.1.0"
4
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-cli-daemon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael Grosser
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-05-28 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: michael@grosser.it
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - MIT-LICENSE
20
+ - bin/ruby-sli-daemon.sh
21
+ - lib/ruby_cli_daemon.rb
22
+ - lib/ruby_cli_daemon/version.rb
23
+ homepage: https://github.com/grosser/ruby-cli-daemon
24
+ licenses:
25
+ - MIT
26
+ metadata: {}
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 2.3.0
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubyforge_project:
43
+ rubygems_version: 2.7.6
44
+ signing_key:
45
+ specification_version: 4
46
+ summary: Preforking daemon that makes all ruby binaries faster
47
+ test_files: []