ruby-cli-daemon 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|