zeus 0.2.0.beta1 → 0.2.0.beta2
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.
- data/Gemfile +2 -0
- data/README.md +24 -13
- data/bin/zeus +1 -40
- data/ext/.DS_Store +0 -0
- data/ext/fsevents-wrapper/fsevents-wrapper +0 -0
- data/ext/fsevents-wrapper/main.m +133 -0
- data/lib/{tiny_thor.rb → thrud.rb} +2 -2
- data/lib/zeus.rb +9 -4
- data/lib/zeus/cli.rb +2 -5
- data/lib/zeus/client.rb +65 -36
- data/lib/zeus/dsl.rb +0 -3
- data/lib/zeus/server.rb +41 -55
- data/lib/zeus/server/acceptor.rb +2 -2
- data/lib/zeus/server/client_handler.rb +6 -15
- data/lib/zeus/server/file_monitor.rb +2 -50
- data/lib/zeus/server/file_monitor/fsevent.rb +63 -0
- data/lib/zeus/server/process_tree_monitor.rb +1 -1
- data/lib/zeus/server/stage.rb +5 -3
- data/lib/zeus/templates/rails.rb +15 -14
- data/lib/zeus/ui.rb +5 -0
- data/lib/zeus/version.rb +1 -1
- data/zeus.gemspec +1 -3
- metadata +8 -16
- data/lib/zeus/init.rb +0 -17
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -17,7 +17,6 @@ Soon? You can use Zeus now, but don't expect it to be perfect. I'm working hard
|
|
17
17
|
## Ugly bits
|
18
18
|
|
19
19
|
* Not battle-tested yet
|
20
|
-
* Creates a bunch of sockets
|
21
20
|
* Uses an obscene number of file descriptors
|
22
21
|
|
23
22
|
## Installation
|
@@ -26,7 +25,17 @@ Install the gem.
|
|
26
25
|
|
27
26
|
gem install zeus
|
28
27
|
|
29
|
-
|
28
|
+
Run the project initializer.
|
29
|
+
|
30
|
+
zeus init
|
31
|
+
|
32
|
+
## Upgrading from initial release
|
33
|
+
|
34
|
+
Since zeus is super-brand-new, the config file format changed already.
|
35
|
+
|
36
|
+
gem install zeus
|
37
|
+
rm .zeus.rb
|
38
|
+
zeus init
|
30
39
|
|
31
40
|
## Usage
|
32
41
|
|
@@ -45,23 +54,25 @@ Run some commands:
|
|
45
54
|
|
46
55
|
## TODO (roughly prioritized)
|
47
56
|
|
48
|
-
*
|
49
|
-
*
|
50
|
-
*
|
51
|
-
* Support other frameworks?
|
52
|
-
* Use fsevent instead of kqueue to reduce the obscene number of file descriptors.
|
53
|
-
* Support inotify on linux
|
57
|
+
* Make sure client connection requests are handled immediately
|
58
|
+
* Acceptors booting should not be dependent on passing all loaded features to the file monitor
|
59
|
+
* Route all logging output through Zeus.ui
|
54
60
|
* Handle connections for not-yet-started sockets
|
61
|
+
* Refactor, refactor, refactor...
|
62
|
+
* Support other frameworks?
|
63
|
+
* Periodically re-attempt to add lost files to KQueue
|
64
|
+
* Figure out how to run full test suites without multiple env loads
|
55
65
|
* Don't replace a socket with changed deps until the new one is ready
|
66
|
+
|
67
|
+
## Ideas (not quite TODOs)
|
68
|
+
|
56
69
|
* (maybe) Start the preloader as a daemon transparently when any command is run, then wait for it to finish
|
70
|
+
* Use fsevent instead of kqueue to reduce the obscene number of file descriptors.
|
71
|
+
* Support inotify on linux
|
57
72
|
|
58
73
|
## Contributing
|
59
74
|
|
60
|
-
|
61
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
62
|
-
3. Commit your changes (`git commit -am 'Added some feature'`)
|
63
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
64
|
-
5. Create new Pull Request
|
75
|
+
Fork, Branch, Pull Request.
|
65
76
|
|
66
77
|
## Thanks...
|
67
78
|
|
data/bin/zeus
CHANGED
@@ -1,43 +1,4 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'zeus'
|
4
|
-
|
5
|
-
Zeus::CLI.start
|
6
|
-
|
7
|
-
begin
|
8
|
-
rescue Zeus::ZeusError => e
|
9
|
-
Zeus.ui.error e.message
|
10
|
-
Zeus.ui.debug e.backtrace.join("\n")
|
11
|
-
exit e.status_code
|
12
|
-
rescue Interrupt => e
|
13
|
-
Zeus.ui.error e.message
|
14
|
-
Zeus.ui.debug e.backtrace.join("\n")
|
15
|
-
exit 1
|
16
|
-
rescue SystemExit => e
|
17
|
-
exit e.status
|
18
|
-
rescue Exception => e
|
19
|
-
Zeus.ui.error("A fatal error has occurred. Please see the github issues at:\n" \
|
20
|
-
"http://github.com/burke/zeus/issues")
|
21
|
-
end
|
22
|
-
exit 0
|
23
|
-
|
24
|
-
|
25
|
-
case ARGV[0]
|
26
|
-
when "start"
|
27
|
-
require 'zeus/server'
|
28
|
-
begin
|
29
|
-
require './.zeus.rb'
|
30
|
-
rescue LoadError
|
31
|
-
puts "\x1b[31mYour project is missing a config file (.zeus.rb), or you are not in the project root."
|
32
|
-
puts "You can run `zeus init` to generate a config file.\x1b[0m"
|
33
|
-
exit 1
|
34
|
-
end
|
35
|
-
Zeus::Server.run
|
36
|
-
when "init"
|
37
|
-
require 'zeus/init'
|
38
|
-
ARGV.shift # get "init" off the list
|
39
|
-
Zeus::Init.run(ARGV)
|
40
|
-
else
|
41
|
-
require 'zeus/client'
|
42
|
-
Zeus::Client.run
|
43
|
-
end
|
4
|
+
Zeus::CLI.start
|
data/ext/.DS_Store
ADDED
Binary file
|
Binary file
|
@@ -0,0 +1,133 @@
|
|
1
|
+
//
|
2
|
+
// main.m
|
3
|
+
// fsevents-wrapper
|
4
|
+
//
|
5
|
+
// Created by Burke Libbey on 2012-07-30.
|
6
|
+
// Copyright (c) 2012 Burke Libbey. All rights reserved.
|
7
|
+
//
|
8
|
+
|
9
|
+
#import <Foundation/Foundation.h>
|
10
|
+
#include <CoreServices/CoreServices.h>
|
11
|
+
#include <sys/stat.h>
|
12
|
+
|
13
|
+
|
14
|
+
static CFMutableArrayRef _watchedFiles;
|
15
|
+
static FSEventStreamRef _activeStream;
|
16
|
+
static NSMutableDictionary *_fileIsWatched;
|
17
|
+
|
18
|
+
static int flagsWorthReloadingFor = \
|
19
|
+
kFSEventStreamEventFlagItemRemoved | \
|
20
|
+
kFSEventStreamEventFlagItemRenamed | \
|
21
|
+
kFSEventStreamEventFlagItemModified;
|
22
|
+
|
23
|
+
void myCallbackFunction(
|
24
|
+
ConstFSEventStreamRef streamRef,
|
25
|
+
void *clientCallBackInfo,
|
26
|
+
size_t numEvents,
|
27
|
+
void *eventPaths,
|
28
|
+
const FSEventStreamEventFlags eventFlags[],
|
29
|
+
const FSEventStreamEventId eventIds[])
|
30
|
+
{
|
31
|
+
int i, flags;
|
32
|
+
char **paths = eventPaths;
|
33
|
+
|
34
|
+
for (i = 0; i < numEvents; i++) {
|
35
|
+
flags = eventFlags[i];
|
36
|
+
|
37
|
+
if (flags & (kFSEventStreamEventFlagItemIsFile | flagsWorthReloadingFor)) {
|
38
|
+
printf("%s\n", paths[i]);
|
39
|
+
fflush(stdout);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
void configureStream()
|
45
|
+
{
|
46
|
+
if (CFArrayGetCount(_watchedFiles) == 0) return;
|
47
|
+
|
48
|
+
if (_activeStream) {
|
49
|
+
FSEventStreamStop(_activeStream);
|
50
|
+
FSEventStreamUnscheduleFromRunLoop(_activeStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
51
|
+
//CFRelease(_activeStream);
|
52
|
+
}
|
53
|
+
|
54
|
+
_activeStream = FSEventStreamCreate(NULL,
|
55
|
+
&myCallbackFunction,
|
56
|
+
NULL,
|
57
|
+
_watchedFiles,
|
58
|
+
kFSEventStreamEventIdSinceNow,
|
59
|
+
1.0, // latency
|
60
|
+
kFSEventStreamCreateFlagFileEvents);
|
61
|
+
|
62
|
+
FSEventStreamScheduleWithRunLoop(_activeStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
63
|
+
|
64
|
+
FSEventStreamStart(_activeStream);
|
65
|
+
|
66
|
+
}
|
67
|
+
|
68
|
+
int maybeAddFileToWatchList(char *line)
|
69
|
+
{
|
70
|
+
CFStringRef file = CFStringCreateWithCString(NULL, line, kCFStringEncodingASCII);
|
71
|
+
struct stat buf;
|
72
|
+
|
73
|
+
if ([_fileIsWatched valueForKey:(__bridge NSString *)file]) {
|
74
|
+
return 0;
|
75
|
+
} else if (stat(line, &buf) == 0) {
|
76
|
+
[_fileIsWatched setValue:@"yes" forKey:(__bridge NSString *)file];
|
77
|
+
CFArrayAppendValue(_watchedFiles, file);
|
78
|
+
return 1;
|
79
|
+
} else {
|
80
|
+
return 0;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
void handleInputFiles()
|
85
|
+
{
|
86
|
+
int anyChanges = 0;
|
87
|
+
|
88
|
+
char line[2048];
|
89
|
+
|
90
|
+
fd_set fdset;
|
91
|
+
|
92
|
+
FD_ZERO(&fdset);
|
93
|
+
FD_SET(STDIN_FILENO, &fdset);
|
94
|
+
struct timeval timeout;
|
95
|
+
timeout.tv_sec = 0;
|
96
|
+
timeout.tv_usec = 0;
|
97
|
+
|
98
|
+
select(STDIN_FILENO+1, &fdset, NULL, NULL, &timeout);
|
99
|
+
while (FD_ISSET(STDIN_FILENO, &fdset) && fgets(line, 2048, stdin) != NULL) {
|
100
|
+
line[strlen(line)-1] = 0;
|
101
|
+
anyChanges |= maybeAddFileToWatchList(line);
|
102
|
+
select(STDIN_FILENO+1, &fdset, NULL, NULL, &timeout);
|
103
|
+
}
|
104
|
+
|
105
|
+
if (anyChanges) {
|
106
|
+
configureStream();
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
void configureTimerAndRun()
|
111
|
+
{
|
112
|
+
CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL,
|
113
|
+
0,
|
114
|
+
0.5,
|
115
|
+
0,
|
116
|
+
0,
|
117
|
+
&handleInputFiles,
|
118
|
+
NULL);
|
119
|
+
|
120
|
+
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
|
121
|
+
CFRunLoopRun();
|
122
|
+
}
|
123
|
+
|
124
|
+
int main(int argc, const char * argv[])
|
125
|
+
{
|
126
|
+
@autoreleasepool {
|
127
|
+
_watchedFiles = CFArrayCreateMutable(NULL, 0, NULL);
|
128
|
+
_fileIsWatched = [[NSMutableDictionary alloc] initWithCapacity:500];
|
129
|
+
|
130
|
+
configureTimerAndRun();
|
131
|
+
}
|
132
|
+
return 0;
|
133
|
+
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class
|
1
|
+
class Thrud
|
2
2
|
|
3
3
|
class Task < Struct.new(:method_name, :desc, :long_desc, :method_options)
|
4
4
|
def arity(obj)
|
@@ -78,7 +78,7 @@ BANNER
|
|
78
78
|
taskname = ARGV.shift
|
79
79
|
arguments = ARGV
|
80
80
|
|
81
|
-
taskname
|
81
|
+
taskname ||= "help"
|
82
82
|
|
83
83
|
unless task = @tasks[taskname.to_s]
|
84
84
|
Zeus.ui.error "Could not find task \"#{taskname}\""
|
data/lib/zeus.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
-
require "zeus/version"
|
2
|
-
require 'zeus/server'
|
3
|
-
require "zeus/ui"
|
4
|
-
|
5
1
|
module Zeus
|
2
|
+
SOCKET_NAME = '.zeus.sock'
|
3
|
+
|
4
|
+
autoload :UI, 'zeus/ui'
|
5
|
+
autoload :CLI, 'zeus/cli'
|
6
|
+
autoload :DSL, 'zeus/dsl'
|
7
|
+
autoload :Server, 'zeus/server'
|
8
|
+
autoload :Client, 'zeus/client'
|
9
|
+
autoload :Version, 'zeus/version'
|
10
|
+
|
6
11
|
class ZeusError < StandardError
|
7
12
|
def self.status_code(code)
|
8
13
|
define_method(:status_code) { code }
|
data/lib/zeus/cli.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
require '
|
2
|
-
require 'tiny_thor'
|
1
|
+
require 'thrud'
|
3
2
|
|
4
3
|
module Zeus
|
5
|
-
class CLI <
|
4
|
+
class CLI < Thrud
|
6
5
|
|
7
6
|
def initialize(*)
|
8
7
|
super
|
@@ -34,7 +33,6 @@ module Zeus
|
|
34
33
|
use the zeus runner commands (see `zeus help` for a list of available commands).
|
35
34
|
D
|
36
35
|
def start
|
37
|
-
require 'zeus/server'
|
38
36
|
begin
|
39
37
|
require './.zeus.rb'
|
40
38
|
rescue LoadError
|
@@ -60,7 +58,6 @@ module Zeus
|
|
60
58
|
Zeus::Server.acceptors.each do |acc|
|
61
59
|
desc acc.name, (acc.description || "#{acc.name} task defined in .zeus.rb")
|
62
60
|
define_method(acc.name) { |*args|
|
63
|
-
require 'zeus/client'
|
64
61
|
Zeus::Client.run(acc.name, args)
|
65
62
|
}
|
66
63
|
map acc.aliases => acc.name
|
data/lib/zeus/client.rb
CHANGED
@@ -6,61 +6,90 @@ require "socket"
|
|
6
6
|
module Zeus
|
7
7
|
class Client
|
8
8
|
|
9
|
+
attr_accessor :pid
|
10
|
+
|
9
11
|
SIGNALS = {
|
10
12
|
"\x03" => "TERM",
|
11
13
|
"\x1C" => "QUIT"
|
12
14
|
}
|
13
15
|
SIGNAL_REGEX = Regexp.union(SIGNALS.keys)
|
14
16
|
|
15
|
-
def self.
|
16
|
-
|
17
|
-
$stdout.raw(&b)
|
18
|
-
else
|
19
|
-
b.call
|
20
|
-
end
|
17
|
+
def self.run(command, args)
|
18
|
+
new.run(command, args)
|
21
19
|
end
|
22
20
|
|
23
|
-
def
|
21
|
+
def run(command, args)
|
24
22
|
maybe_raw do
|
25
23
|
PTY.open do |master, slave|
|
26
|
-
|
27
|
-
|
28
|
-
trap("WINCH") { winch_ << "\0" }
|
24
|
+
@master = master
|
25
|
+
set_winsize
|
29
26
|
|
30
|
-
|
31
|
-
|
32
|
-
socket.send_io(slave)
|
33
|
-
slave.close
|
34
|
-
|
35
|
-
pid = socket.readline.chomp.to_i
|
27
|
+
@winch = make_winch_channel
|
28
|
+
pid = connect_to_server(command, args, slave)
|
36
29
|
|
30
|
+
buffer = ""
|
37
31
|
begin
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
if ready.include?(
|
42
|
-
winch.read(1)
|
43
|
-
$stdout.tty? and master.winsize = $stdout.winsize
|
44
|
-
Process.kill("WINCH", pid)
|
45
|
-
end
|
46
|
-
|
47
|
-
if ready.include?($stdin)
|
48
|
-
input = $stdin.readpartial(4096, buffer)
|
49
|
-
input.scan(SIGNAL_REGEX).each { |signal|
|
50
|
-
Process.kill(SIGNALS[signal], pid)
|
51
|
-
}
|
52
|
-
master << input
|
53
|
-
end
|
54
|
-
|
55
|
-
if ready.include?(master)
|
56
|
-
$stdout << master.readpartial(4096, buffer)
|
57
|
-
end
|
32
|
+
while ready = select([@winch, @master, $stdin])[0]
|
33
|
+
handle_winch if ready.include?(@winch)
|
34
|
+
handle_stdin(buffer) if ready.include?($stdin)
|
35
|
+
handle_master(buffer) if ready.include?(@master)
|
58
36
|
end
|
59
37
|
rescue EOFError
|
60
38
|
end
|
61
39
|
end
|
62
40
|
end
|
63
41
|
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def connect_to_server(command, arguments, slave, socket_path = Zeus::SOCKET_NAME)
|
46
|
+
socket = UNIXSocket.new(socket_path)
|
47
|
+
socket << {command: command, arguments: arguments}.to_json << "\n"
|
48
|
+
socket.send_io(slave)
|
49
|
+
slave.close
|
50
|
+
|
51
|
+
pid = socket.readline.chomp.to_i
|
52
|
+
rescue Errno::ENOENT, Errno::ECONNREFUSED, Errno::ECONNRESET
|
53
|
+
Zeus.ui.error "Zeus doesn't seem to be running, try 'zeus start`"
|
54
|
+
abort
|
55
|
+
end
|
56
|
+
|
57
|
+
def make_winch_channel
|
58
|
+
winch, winch_ = IO.pipe
|
59
|
+
trap("WINCH") { winch_ << "\0" }
|
60
|
+
winch
|
61
|
+
end
|
62
|
+
|
63
|
+
def handle_winch
|
64
|
+
@winch.read(1)
|
65
|
+
set_winsize
|
66
|
+
Process.kill("WINCH", pid) if pid
|
67
|
+
end
|
68
|
+
|
69
|
+
def handle_stdin(buffer)
|
70
|
+
input = $stdin.readpartial(4096, buffer)
|
71
|
+
input.scan(SIGNAL_REGEX).each { |signal|
|
72
|
+
Process.kill(SIGNALS[signal], pid)
|
73
|
+
}
|
74
|
+
@master << input
|
75
|
+
end
|
76
|
+
|
77
|
+
def handle_master(buffer)
|
78
|
+
$stdout << @master.readpartial(4096, buffer)
|
79
|
+
end
|
80
|
+
|
81
|
+
def set_winsize
|
82
|
+
$stdout.tty? and @master.winsize = $stdout.winsize
|
83
|
+
end
|
84
|
+
|
85
|
+
def maybe_raw(&b)
|
86
|
+
if $stdout.tty?
|
87
|
+
$stdout.raw(&b)
|
88
|
+
else
|
89
|
+
b.call
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
64
93
|
end
|
65
94
|
end
|
66
95
|
|
data/lib/zeus/dsl.rb
CHANGED
data/lib/zeus/server.rb
CHANGED
@@ -2,18 +2,18 @@ require 'json'
|
|
2
2
|
require 'socket'
|
3
3
|
|
4
4
|
require 'rb-kqueue'
|
5
|
-
|
6
5
|
require 'zeus/process'
|
7
|
-
require 'zeus/dsl'
|
8
|
-
require 'zeus/server/file_monitor'
|
9
|
-
require 'zeus/server/client_handler'
|
10
|
-
require 'zeus/server/process_tree_monitor'
|
11
|
-
require 'zeus/server/acceptor_registration_monitor'
|
12
|
-
require 'zeus/server/acceptor'
|
13
6
|
|
14
7
|
module Zeus
|
15
8
|
class Server
|
16
9
|
|
10
|
+
autoload :Stage, 'zeus/server/stage'
|
11
|
+
autoload :Acceptor, 'zeus/server/acceptor'
|
12
|
+
autoload :FileMonitor, 'zeus/server/file_monitor'
|
13
|
+
autoload :ClientHandler, 'zeus/server/client_handler'
|
14
|
+
autoload :ProcessTreeMonitor, 'zeus/server/process_tree_monitor'
|
15
|
+
autoload :AcceptorRegistrationMonitor, 'zeus/server/acceptor_registration_monitor'
|
16
|
+
|
17
17
|
def self.define!(&b)
|
18
18
|
@@definition = Zeus::DSL::Evaluator.new.instance_eval(&b)
|
19
19
|
end
|
@@ -24,7 +24,7 @@ module Zeus
|
|
24
24
|
|
25
25
|
attr_reader :client_handler, :acceptor_registration_monitor
|
26
26
|
def initialize
|
27
|
-
@file_monitor = FileMonitor.new(&method(:dependency_did_change))
|
27
|
+
@file_monitor = FileMonitor::FSEvent.new(&method(:dependency_did_change))
|
28
28
|
@acceptor_registration_monitor = AcceptorRegistrationMonitor.new
|
29
29
|
@process_tree_monitor = ProcessTreeMonitor.new
|
30
30
|
@client_handler = ClientHandler.new(acceptor_registration_monitor)
|
@@ -39,22 +39,18 @@ module Zeus
|
|
39
39
|
|
40
40
|
PID_TYPE = "P"
|
41
41
|
def w_pid line
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
retry
|
47
|
-
end
|
42
|
+
@w_msg.send(PID_TYPE + line, 0)
|
43
|
+
rescue Errno::ENOBUFS
|
44
|
+
sleep 0.2
|
45
|
+
retry
|
48
46
|
end
|
49
47
|
|
50
48
|
FEATURE_TYPE = "F"
|
51
49
|
def w_feature line
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
retry
|
57
|
-
end
|
50
|
+
@w_msg.send(FEATURE_TYPE + line, 0)
|
51
|
+
rescue Errno::ENOBUFS
|
52
|
+
sleep 0.2
|
53
|
+
retry
|
58
54
|
end
|
59
55
|
|
60
56
|
def run
|
@@ -69,48 +65,38 @@ module Zeus
|
|
69
65
|
@w_msg.close
|
70
66
|
|
71
67
|
loop do
|
72
|
-
@file_monitor
|
73
|
-
|
74
|
-
datasources = [@r_msg,
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
rs, _, _ = IO.select(datasources, [], [], 1)
|
81
|
-
rescue Errno::EBADF
|
82
|
-
puts "EBADF" unless defined?($asdf)
|
83
|
-
sleep 1
|
84
|
-
$asdf = true
|
68
|
+
monitors = [@file_monitor, @acceptor_registration_monitor, @client_handler]
|
69
|
+
# TODO: Make @r_msg a Monitor instead. All that logic should be its own thing.
|
70
|
+
datasources = [@r_msg, *monitors.map(&:datasource)]
|
71
|
+
|
72
|
+
ready, _, _ = IO.select(datasources, [], [], 1)
|
73
|
+
next unless ready
|
74
|
+
monitors.each do |m|
|
75
|
+
m.on_datasource_event if ready.include?(m.datasource)
|
85
76
|
end
|
86
|
-
|
87
|
-
case r
|
88
|
-
when @acceptor_registration_monitor.datasource
|
89
|
-
@acceptor_registration_monitor.on_datasource_event
|
90
|
-
when @r_msg ; handle_messages
|
91
|
-
when @client_handler.datasource
|
92
|
-
@client_handler.on_datasource_event
|
93
|
-
end
|
94
|
-
end if rs
|
77
|
+
handle_messages if ready.include?(@r_msg)
|
95
78
|
end
|
96
79
|
|
80
|
+
ensure
|
81
|
+
File.unlink(Zeus::SOCKET_NAME)
|
97
82
|
end
|
98
83
|
|
99
84
|
def handle_messages
|
100
85
|
loop do
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
86
|
+
handle_message
|
87
|
+
end
|
88
|
+
rescue Errno::EAGAIN
|
89
|
+
end
|
90
|
+
|
91
|
+
def handle_message
|
92
|
+
data = @r_msg.recv_nonblock(1024)
|
93
|
+
case data[0]
|
94
|
+
when FEATURE_TYPE
|
95
|
+
handle_feature_message(data[1..-1])
|
96
|
+
when PID_TYPE
|
97
|
+
handle_pid_message(data[1..-1])
|
98
|
+
else
|
99
|
+
raise "Unrecognized message"
|
114
100
|
end
|
115
101
|
end
|
116
102
|
|
data/lib/zeus/server/acceptor.rb
CHANGED
@@ -36,9 +36,9 @@ module Zeus
|
|
36
36
|
|
37
37
|
@server.w_pid "#{pid}:#{Process.ppid}"
|
38
38
|
|
39
|
-
|
39
|
+
Zeus.ui.as_zeus "starting acceptor `#{@name}`"
|
40
40
|
trap("INT") {
|
41
|
-
|
41
|
+
Zeus.ui.as_zeus "killing acceptor `#{@name}`"
|
42
42
|
exit 0
|
43
43
|
}
|
44
44
|
|
@@ -1,8 +1,6 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'json'
|
3
3
|
|
4
|
-
require 'zeus/server/acceptor'
|
5
|
-
|
6
4
|
module Zeus
|
7
5
|
class Server
|
8
6
|
# The model here is kind of convoluted, so here's an explanation of what's
|
@@ -26,23 +24,16 @@ module Zeus
|
|
26
24
|
# 4. ClientHandler forwards the pid to the client over S_CLI.
|
27
25
|
#
|
28
26
|
class ClientHandler
|
29
|
-
SERVER_SOCK = ".zeus.sock"
|
30
|
-
|
31
27
|
def datasource ; @server ; end
|
32
28
|
def on_datasource_event ; handle_server_connection ; end
|
33
29
|
|
34
30
|
def initialize(registration_monitor)
|
35
31
|
@reg_monitor = registration_monitor
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
exit 1
|
42
|
-
# ensure
|
43
|
-
# @server.close rescue nil
|
44
|
-
# File.unlink(SERVER_SOCK)
|
45
|
-
end
|
32
|
+
@server = UNIXServer.new(Zeus::SOCKET_NAME)
|
33
|
+
@server.listen(10)
|
34
|
+
rescue Errno::EADDRINUSE
|
35
|
+
Zeus.ui.error "Zeus appears to be already running in this project. If not, remove .zeus.sock and try again."
|
36
|
+
exit 1
|
46
37
|
end
|
47
38
|
|
48
39
|
def handle_server_connection
|
@@ -71,7 +62,7 @@ module Zeus
|
|
71
62
|
# TODO handle nothing found
|
72
63
|
usock.send_io(client_terminal)
|
73
64
|
|
74
|
-
|
65
|
+
Zeus.ui.info "accepting connection for #{command}"
|
75
66
|
|
76
67
|
# 4
|
77
68
|
acceptor.socket.puts arguments.to_json
|
@@ -2,56 +2,8 @@ require 'rb-kqueue'
|
|
2
2
|
|
3
3
|
module Zeus
|
4
4
|
class Server
|
5
|
-
|
6
|
-
|
7
|
-
TARGET_FD_LIMIT = 8192
|
8
|
-
|
9
|
-
def initialize(&change_callback)
|
10
|
-
configure_file_descriptor_resource_limit
|
11
|
-
@queue = KQueue::Queue.new
|
12
|
-
@watched_files = {}
|
13
|
-
@deleted_files = []
|
14
|
-
@change_callback = change_callback
|
15
|
-
end
|
16
|
-
|
17
|
-
def process_events
|
18
|
-
@queue.poll
|
19
|
-
end
|
20
|
-
|
21
|
-
def watch(file)
|
22
|
-
return if @watched_files[file]
|
23
|
-
@watched_files[file] = true
|
24
|
-
@queue.watch_file(file, :write, :extend, :rename, :delete, &method(:file_did_change))
|
25
|
-
rescue Errno::ENOENT
|
26
|
-
Zeus.ui.debug("No file found at #{file}")
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def file_did_change(event)
|
32
|
-
Zeus.ui.info("Dependency change at #{event.watcher.path}")
|
33
|
-
resubscribe_deleted_file(event) if event.flags.include?(:delete)
|
34
|
-
@change_callback.call(event.watcher.path)
|
35
|
-
end
|
36
|
-
|
37
|
-
def configure_file_descriptor_resource_limit
|
38
|
-
limit = Process.getrlimit(Process::RLIMIT_NOFILE)
|
39
|
-
if limit[0] < TARGET_FD_LIMIT && limit[1] >= TARGET_FD_LIMIT
|
40
|
-
Process.setrlimit(Process::RLIMIT_NOFILE, TARGET_FD_LIMIT)
|
41
|
-
else
|
42
|
-
puts "\x1b[33m[zeus] Warning: increase the max number of file descriptors. If you have a large project, this max cause a crash in about 10 seconds.\x1b[0m"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def resubscribe_deleted_file(event)
|
47
|
-
event.watcher.disable!
|
48
|
-
begin
|
49
|
-
@queue.watch_file(event.watcher.path, :write, :extend, :rename, :delete, &method(:file_did_change))
|
50
|
-
rescue Errno::ENOENT
|
51
|
-
@deleted_files << event.watcher.path
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
5
|
+
module FileMonitor
|
6
|
+
autoload :FSEvent, 'zeus/server/file_monitor/fsevent'
|
55
7
|
end
|
56
8
|
end
|
57
9
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module Zeus
|
4
|
+
class Server
|
5
|
+
module FileMonitor
|
6
|
+
class FSEvent
|
7
|
+
WRAPPER_PATH = File.expand_path("../../../../../ext/fsevents-wrapper/fsevents-wrapper", __FILE__)
|
8
|
+
|
9
|
+
def datasource ; @io_out ; end
|
10
|
+
def on_datasource_event ; handle_changed_files ; end
|
11
|
+
|
12
|
+
def initialize(&change_callback)
|
13
|
+
@change_callback = change_callback
|
14
|
+
@io_in, @io_out, _ = Open3.popen2e(WRAPPER_PATH)
|
15
|
+
@watched_files = {}
|
16
|
+
@buffer = ""
|
17
|
+
end
|
18
|
+
|
19
|
+
def handle_changed_files
|
20
|
+
loop do
|
21
|
+
begin
|
22
|
+
read_and_notify_files
|
23
|
+
rescue Errno::EAGAIN
|
24
|
+
break
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def read_and_notify_files
|
30
|
+
lines = @io_out.read_nonblock(30000)
|
31
|
+
files = lines.split("\n")
|
32
|
+
files[0] = "#{@buffer}#{files[0]}" unless @buffer == ""
|
33
|
+
unless lines[-1] == "\n"
|
34
|
+
@buffer = files.pop
|
35
|
+
end
|
36
|
+
|
37
|
+
files.each do |file|
|
38
|
+
file_did_change(file)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def watch(file)
|
43
|
+
return false if @watched_files[file]
|
44
|
+
@watched_files[file] = true
|
45
|
+
File.open('a.log', 'a') { |f| f.puts file }
|
46
|
+
@io_in.puts file
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def file_did_change(file)
|
53
|
+
Zeus.ui.info("Dependency change at #{file}")
|
54
|
+
@change_callback.call(file)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
|
@@ -76,7 +76,7 @@ module Zeus
|
|
76
76
|
def kill_nodes_with_feature(file, base = @root)
|
77
77
|
if base.has_feature?(file)
|
78
78
|
if base == @root.children[0] || base == @root
|
79
|
-
|
79
|
+
Zeus.ui.error "One of zeus's dependencies changed. Not killing zeus. You may have to restart the server."
|
80
80
|
return false
|
81
81
|
end
|
82
82
|
kill_node(base)
|
data/lib/zeus/server/stage.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Zeus
|
2
2
|
class Server
|
3
3
|
class Stage
|
4
|
+
HasNoChildren = Class.new(Exception)
|
4
5
|
|
5
6
|
attr_accessor :name, :stages, :actions
|
6
7
|
attr_reader :pid
|
@@ -17,9 +18,10 @@ module Zeus
|
|
17
18
|
$0 = "zeus spawner: #{@name}"
|
18
19
|
pid = Process.pid
|
19
20
|
@server.w_pid "#{pid}:#{Process.ppid}"
|
20
|
-
|
21
|
+
|
22
|
+
Zeus.ui.as_zeus("starting spawner `#{@name}`")
|
21
23
|
trap("INT") {
|
22
|
-
|
24
|
+
Zeus.ui.as_zeus("killing spawner `#{@name}`")
|
23
25
|
exit 0
|
24
26
|
}
|
25
27
|
|
@@ -38,7 +40,7 @@ module Zeus
|
|
38
40
|
begin
|
39
41
|
pid = Process.wait
|
40
42
|
rescue Errno::ECHILD
|
41
|
-
raise "Stage `#{@name}`
|
43
|
+
raise HasNoChildren.new("Stage `#{@name}` - All terminal nodes must be acceptors")
|
42
44
|
end
|
43
45
|
if (status = $?.exitstatus) > 0
|
44
46
|
exit status
|
data/lib/zeus/templates/rails.rb
CHANGED
@@ -19,7 +19,7 @@ Zeus::Server.define! do
|
|
19
19
|
stage :dev do
|
20
20
|
action do
|
21
21
|
Bundler.require(:development)
|
22
|
-
ENV['RAILS_ENV'] = "development"
|
22
|
+
Rails.env = ENV['RAILS_ENV'] = "development"
|
23
23
|
require APP_PATH
|
24
24
|
Rails.application.require_environment!
|
25
25
|
end
|
@@ -57,22 +57,23 @@ Zeus::Server.define! do
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
stage :test do
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
60
|
+
# stage :test do
|
61
|
+
# action do
|
62
|
+
# Rails.env = ENV['RAILS_ENV'] = "test"
|
63
|
+
# Bundler.require(:test)
|
64
|
+
# require APP_PATH
|
65
|
+
# Rails.application.require_environment!
|
66
|
+
# end
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
68
|
+
# command :testrb do
|
69
|
+
# (r = Test::Unit::AutoRunner.new(true)).process_args(ARGV) or
|
70
|
+
# abort r.options.banner + " tests..."
|
71
|
+
# exit r.run
|
72
|
+
# end
|
73
73
|
|
74
|
-
end
|
74
|
+
# end
|
75
75
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
79
|
+
|
data/lib/zeus/ui.rb
CHANGED
@@ -6,6 +6,10 @@ module Zeus
|
|
6
6
|
@debug = ENV['DEBUG']
|
7
7
|
end
|
8
8
|
|
9
|
+
def as_zeus(msg)
|
10
|
+
tell_me("[zeus] #{msg}",:purple)
|
11
|
+
end
|
12
|
+
|
9
13
|
def info(msg)
|
10
14
|
tell_me(msg, nil) if !@quiet
|
11
15
|
end
|
@@ -45,6 +49,7 @@ module Zeus
|
|
45
49
|
when :red ; "\x1b[31m#{msg}\x1b[0m"
|
46
50
|
when :green ; "\x1b[32m#{msg}\x1b[0m"
|
47
51
|
when :yellow ; "\x1b[33m#{msg}\x1b[0m"
|
52
|
+
when :purple ; "\x1b[35m#{msg}\x1b[0m"
|
48
53
|
else ; msg
|
49
54
|
end
|
50
55
|
puts msg
|
data/lib/zeus/version.rb
CHANGED
data/zeus.gemspec
CHANGED
@@ -8,12 +8,10 @@ Gem::Specification.new do |gem|
|
|
8
8
|
gem.summary = %q{Zeus is an alpha-quality application preloader with terrible documentation.}
|
9
9
|
gem.homepage = "http://github.com/burke/zeus"
|
10
10
|
|
11
|
-
gem.files = `git ls-files`.split(
|
11
|
+
gem.files = `git ls-files`.split("\n").reject{ |f| f =~ /xcodeproj/ }
|
12
12
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
13
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
14
|
gem.name = "zeus"
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Zeus::VERSION
|
17
|
-
|
18
|
-
gem.add_dependency "rb-kqueue-burke", "~> 0.1.0"
|
19
17
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zeus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.0.
|
4
|
+
version: 0.2.0.beta2
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,19 +9,8 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-07-
|
13
|
-
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: rb-kqueue-burke
|
16
|
-
requirement: &70116037263980 !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ~>
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: 0.1.0
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: *70116037263980
|
12
|
+
date: 2012-07-31 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
25
14
|
description: Zeus preloads pretty much everything you'll ever want to use in development.
|
26
15
|
email:
|
27
16
|
- burke@libbey.me
|
@@ -36,18 +25,21 @@ files:
|
|
36
25
|
- README.md
|
37
26
|
- Rakefile
|
38
27
|
- bin/zeus
|
39
|
-
-
|
28
|
+
- ext/.DS_Store
|
29
|
+
- ext/fsevents-wrapper/fsevents-wrapper
|
30
|
+
- ext/fsevents-wrapper/main.m
|
31
|
+
- lib/thrud.rb
|
40
32
|
- lib/zeus.rb
|
41
33
|
- lib/zeus/cli.rb
|
42
34
|
- lib/zeus/client.rb
|
43
35
|
- lib/zeus/dsl.rb
|
44
|
-
- lib/zeus/init.rb
|
45
36
|
- lib/zeus/process.rb
|
46
37
|
- lib/zeus/server.rb
|
47
38
|
- lib/zeus/server/acceptor.rb
|
48
39
|
- lib/zeus/server/acceptor_registration_monitor.rb
|
49
40
|
- lib/zeus/server/client_handler.rb
|
50
41
|
- lib/zeus/server/file_monitor.rb
|
42
|
+
- lib/zeus/server/file_monitor/fsevent.rb
|
51
43
|
- lib/zeus/server/process_tree_monitor.rb
|
52
44
|
- lib/zeus/server/stage.rb
|
53
45
|
- lib/zeus/templates/rails.rb
|
data/lib/zeus/init.rb
DELETED