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 +7 -0
- data/MIT-LICENSE +20 -0
- data/bin/ruby-sli-daemon.sh +68 -0
- data/lib/ruby_cli_daemon.rb +78 -0
- data/lib/ruby_cli_daemon/version.rb +4 -0
- metadata +47 -0
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
|
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: []
|