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 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: []