Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

WIP

  • Loading branch information...
commit 656581eeff98154256967cd1ee222609a29ac5ad 1 parent 9f861bb
@burke authored
Showing with 142 additions and 2,845 deletions.
  1. +1 −0  .gitignore
  2. +0 −5 .travis.yml
  3. +0 −11 .zeus.rb
  4. +0 −8 Gemfile
  5. +2 −1  MIT-LICENSE
  6. +7 −0 Makefile
  7. +0 −15 Rakefile
  8. +8 −0 a.rb
  9. +0 −8 bin/zeus
  10. +7 −0 cmd/zeus/Makefile
  11. +10 −0 cmd/zeus/zeus.go
  12. +0 −14 docs/acceptor_registration.md
  13. +0 −25 docs/client_server_handshake.md
  14. +0 −246 ext/fsevents-wrapper.xcodeproj/project.pbxproj
  15. +0 −7 ext/fsevents-wrapper.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  16. BIN  ext/fsevents-wrapper.xcodeproj/project.xcworkspace/xcuserdata/burke.xcuserdatad/UserInterfaceState.xcuserstate
  17. +0 −5 ext/fsevents-wrapper.xcodeproj/xcuserdata/burke.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist
  18. +0 −86 ext/fsevents-wrapper.xcodeproj/xcuserdata/burke.xcuserdatad/xcschemes/fsevents-wrapper 2.xcscheme
  19. +0 −86 ext/fsevents-wrapper.xcodeproj/xcuserdata/burke.xcuserdatad/xcschemes/fsevents-wrapper.xcscheme
  20. +0 −27 ext/fsevents-wrapper.xcodeproj/xcuserdata/burke.xcuserdatad/xcschemes/xcschememanagement.plist
  21. BIN  ext/fsevents-wrapper/fsevents-wrapper
  22. +0 −118 ext/fsevents-wrapper/main.m
  23. +0 −97 lib/thrud.rb
  24. +0 −47 lib/zeus.rb
  25. +0 −80 lib/zeus/cli.rb
  26. +0 −114 lib/zeus/client.rb
  27. +0 −28 lib/zeus/client/winsize.rb
  28. +0 −16 lib/zeus/error_printer.rb
  29. +0 −18 lib/zeus/plan.rb
  30. +0 −38 lib/zeus/plan/acceptor.rb
  31. +0 −66 lib/zeus/plan/node.rb
  32. +0 −50 lib/zeus/plan/stage.rb
  33. +0 −104 lib/zeus/server.rb
  34. +0 −82 lib/zeus/server/acceptor.rb
  35. +0 −75 lib/zeus/server/acceptor_registration_monitor.rb
  36. +0 −36 lib/zeus/server/at_exit_hack.rb
  37. +0 −106 lib/zeus/server/client_handler.rb
  38. +0 −84 lib/zeus/server/command_runner.rb
  39. +0 −8 lib/zeus/server/file_monitor.rb
  40. +0 −102 lib/zeus/server/file_monitor/fsevent.rb
  41. +0 −40 lib/zeus/server/load_tracking.rb
  42. +0 −89 lib/zeus/server/process_tree_monitor.rb
  43. +0 −88 lib/zeus/server/stage.rb
  44. +0 −42 lib/zeus/server/stage/error_state.rb
  45. +0 −38 lib/zeus/server/stage/feature_notifier.rb
  46. +0 −132 lib/zeus/templates/rails.rb
  47. +0 −57 lib/zeus/ui.rb
  48. +0 −3  lib/zeus/version.rb
  49. +0 −95 spec/cli_spec.rb
  50. +0 −27 spec/error_printer_spec.rb
  51. +0 −106 spec/integration_spec.rb
  52. +0 −88 spec/server/file_monitor/fsevent_spec.rb
  53. +0 −67 spec/server/load_tracking_spec.rb
  54. +0 −50 spec/server/process_tree_monitor_spec.rb
  55. +0 −38 spec/spec_helper.rb
  56. +0 −54 spec/ui_spec.rb
  57. +0 −18 zeus.gemspec
  58. +107 −0 zeus/zeus.go
View
1  .gitignore
@@ -15,3 +15,4 @@ spec/reports
test/tmp
test/version_tmp
tmp
+cmd/zeus/zeus
View
5 .travis.yml
@@ -1,5 +0,0 @@
-language: ruby
-rvm:
- - 1.9.3
- - 1.9.2
-
View
11 .zeus.rb
@@ -1,11 +0,0 @@
-Zeus::Server.define! do
-
- stage :zeus do
- action { require 'zeus' ; require 'rspec' ; require 'rspec/core/runner' }
-
- command :spec, :s do
- RSpec::Core::Runner.autorun
- end
- end
-
-end
View
8 Gemfile
@@ -1,8 +0,0 @@
-source 'https://rubygems.org'
-
-# Specify your gem's dependencies in zeus.gemspec
-gemspec
-
-gem 'rspec'
-gem 'rake'
-gem 'pry'
View
3  MIT-LICENSE
@@ -19,4 +19,5 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
View
7 Makefile
@@ -0,0 +1,7 @@
+MAKEFLAGS = -s
+
+all:
+ cd cmd/zeus; $(MAKE)
+
+clean:
+ cd cmd/zeus; $(MAKE) clean
View
15 Rakefile
@@ -1,15 +0,0 @@
-#!/usr/bin/env rake
-require "bundler/gem_tasks"
-
-require 'rspec/core/rake_task'
-task :spec do
- desc "Run specs under spec/"
- RSpec::Core::RakeTask.new do |t|
- t.pattern = 'spec/**/*_spec.rb'
- end
-end
-
-task :default do
- puts "rake fails when run via bundle exec"
- Rake::Task["spec"].invoke
-end
View
8 a.rb
@@ -0,0 +1,8 @@
+require 'socket'
+
+fd = ENV['ZEUS_MASTER_FD'].to_i
+a,b = UNIXSocket.pair
+sock = UNIXSocket.for_fd(fd)
+sock.send_io(a)
+b << "Hello from ruby!"
+b.close
View
8 bin/zeus
@@ -1,8 +0,0 @@
-#!/usr/bin/env ruby
-
-if defined?(Bundler)
- puts "\x1b[34mDon't run Zeus with `bundle exec`. It's unnecessarily slow.\x1b[0m"
-end
-
-require 'zeus'
-Zeus::CLI.start
View
7 cmd/zeus/Makefile
@@ -0,0 +1,7 @@
+MAKEFLAGS = -s
+
+all:
+ go build
+
+clean:
+ go clean
View
10 cmd/zeus/zeus.go
@@ -0,0 +1,10 @@
+package main
+
+import (
+ "github.com/burke/zeus/zeus"
+ "fmt"
+)
+
+func main () {
+ fmt.Println(zeus.Run())
+}
View
14 docs/acceptor_registration.md
@@ -1,14 +0,0 @@
-# Acceptor Registration
-
-When an acceptor is booted, it registers itself with the master process through UNIX Sockets. Specifically, it talks to the `AcceptorRegistrationMonitor`.
-
-Here's an overview of the registration process:
-
-1. `AcceptorRegistrationMonitor` creates a socketpair for Acceptor registration (`S_REG`)
-2. When an `Acceptor` is started, it:
- 1. Creates a new socketpair for communication with the master process (`S_ACC`)
- 2. Sends one side of `S_ACC` over `S_REG` to the master.
- 3. Sends its pid and then a newline character over `S_REG`.
-3. `AcceptorRegistrationMonitor` receives first the IO and then the pid, and stores them for later reference.
-
-
View
25 docs/client_server_handshake.md
@@ -1,25 +0,0 @@
-# Client/Server handshake
-
-This takes place in `lib/zeus/server/client_handler.rb`, `lib/zeus/client.rb`, and `lib/zeus/server/acceptor.rb`.
-
-The model is kind of convoluted, so here's an explanation of what's happening with all these sockets:
-
-## Running a command
-1. ClientHandler has a UNIXServer (`SVR`) listening.
-2. ClientHandler has a socketpair with the acceptor referenced by the command (see `docs/acceptor_registration.md`) (`S_ACC`)
-3. When clienthandler receives a connection (`S_CLI`) on `SVR`:
- 1. ClientHandler sends `S_CLI` over `S_ACC`, so the acceptor can communicate with the server's client.
- 2. ClientHandler sends a JSON-encoded array of `arguments` over `S_ACC`
- 3. Acceptor sends the newly-forked worker PID over `S_ACC` to clienthandler.
- 4. ClientHandler forwards the pid to the client over `S_CLI`.
-
-
-## A sort of network diagram
- client clienthandler acceptor
- 1 ----------> | {command: String, arguments: [String]}
- 2 ----------> | Terminal IO
- 3 -----------> | Terminal IO
- 4 -----------> | Arguments (json array)
- 5 <----------- | pid
- 6 <--------- | pid
-
View
246 ext/fsevents-wrapper.xcodeproj/project.pbxproj
@@ -1,246 +0,0 @@
-// !$*UTF8*$!
-{
- archiveVersion = 1;
- classes = {
- };
- objectVersion = 46;
- objects = {
-
-/* Begin PBXBuildFile section */
- 8280E8D615C7303000CDF59C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8280E8D515C7303000CDF59C /* main.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
- 82DC011D15C6EE760098E42A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 82DC011C15C6EE760098E42A /* Foundation.framework */; };
- 82DC012B15C6F2620098E42A /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 82DC012A15C6F2620098E42A /* CoreServices.framework */; };
-/* End PBXBuildFile section */
-
-/* Begin PBXCopyFilesBuildPhase section */
- 82DC011615C6EE760098E42A /* CopyFiles */ = {
- isa = PBXCopyFilesBuildPhase;
- buildActionMask = 2147483647;
- dstPath = /usr/share/man/man1/;
- dstSubfolderSpec = 0;
- files = (
- );
- runOnlyForDeploymentPostprocessing = 1;
- };
-/* End PBXCopyFilesBuildPhase section */
-
-/* Begin PBXFileReference section */
- 8280E8D515C7303000CDF59C /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
- 82DC011815C6EE760098E42A /* fsevents-wrapper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "fsevents-wrapper"; sourceTree = BUILT_PRODUCTS_DIR; };
- 82DC011C15C6EE760098E42A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
- 82DC012215C6EE760098E42A /* fsevents-wrapper-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "fsevents-wrapper-Prefix.pch"; sourceTree = "<group>"; };
- 82DC012A15C6F2620098E42A /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
-/* End PBXFileReference section */
-
-/* Begin PBXFrameworksBuildPhase section */
- 82DC011515C6EE760098E42A /* Frameworks */ = {
- isa = PBXFrameworksBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 82DC012B15C6F2620098E42A /* CoreServices.framework in Frameworks */,
- 82DC011D15C6EE760098E42A /* Foundation.framework in Frameworks */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXFrameworksBuildPhase section */
-
-/* Begin PBXGroup section */
- 82DC010D15C6EE760098E42A = {
- isa = PBXGroup;
- children = (
- 82DC011E15C6EE760098E42A /* fsevents-wrapper */,
- 82DC011B15C6EE760098E42A /* Frameworks */,
- 82DC011915C6EE760098E42A /* Products */,
- );
- sourceTree = "<group>";
- };
- 82DC011915C6EE760098E42A /* Products */ = {
- isa = PBXGroup;
- children = (
- 82DC011815C6EE760098E42A /* fsevents-wrapper */,
- );
- name = Products;
- sourceTree = "<group>";
- };
- 82DC011B15C6EE760098E42A /* Frameworks */ = {
- isa = PBXGroup;
- children = (
- 82DC012A15C6F2620098E42A /* CoreServices.framework */,
- 82DC011C15C6EE760098E42A /* Foundation.framework */,
- );
- name = Frameworks;
- sourceTree = "<group>";
- };
- 82DC011E15C6EE760098E42A /* fsevents-wrapper */ = {
- isa = PBXGroup;
- children = (
- 8280E8D515C7303000CDF59C /* main.m */,
- 82DC012115C6EE760098E42A /* Supporting Files */,
- );
- path = "fsevents-wrapper";
- sourceTree = "<group>";
- };
- 82DC012115C6EE760098E42A /* Supporting Files */ = {
- isa = PBXGroup;
- children = (
- 82DC012215C6EE760098E42A /* fsevents-wrapper-Prefix.pch */,
- );
- name = "Supporting Files";
- sourceTree = "<group>";
- };
-/* End PBXGroup section */
-
-/* Begin PBXNativeTarget section */
- 82DC011715C6EE760098E42A /* fsevents-wrapper */ = {
- isa = PBXNativeTarget;
- buildConfigurationList = 82DC012715C6EE760098E42A /* Build configuration list for PBXNativeTarget "fsevents-wrapper" */;
- buildPhases = (
- 82DC011415C6EE760098E42A /* Sources */,
- 82DC011515C6EE760098E42A /* Frameworks */,
- 82DC011615C6EE760098E42A /* CopyFiles */,
- );
- buildRules = (
- );
- dependencies = (
- );
- name = "fsevents-wrapper";
- productName = "fsevents-wrapper";
- productReference = 82DC011815C6EE760098E42A /* fsevents-wrapper */;
- productType = "com.apple.product-type.tool";
- };
-/* End PBXNativeTarget section */
-
-/* Begin PBXProject section */
- 82DC010F15C6EE760098E42A /* Project object */ = {
- isa = PBXProject;
- attributes = {
- LastUpgradeCheck = 0440;
- ORGANIZATIONNAME = "Burke Libbey";
- };
- buildConfigurationList = 82DC011215C6EE760098E42A /* Build configuration list for PBXProject "fsevents-wrapper" */;
- compatibilityVersion = "Xcode 3.2";
- developmentRegion = English;
- hasScannedForEncodings = 0;
- knownRegions = (
- en,
- );
- mainGroup = 82DC010D15C6EE760098E42A;
- productRefGroup = 82DC011915C6EE760098E42A /* Products */;
- projectDirPath = "";
- projectRoot = "";
- targets = (
- 82DC011715C6EE760098E42A /* fsevents-wrapper */,
- );
- };
-/* End PBXProject section */
-
-/* Begin PBXSourcesBuildPhase section */
- 82DC011415C6EE760098E42A /* Sources */ = {
- isa = PBXSourcesBuildPhase;
- buildActionMask = 2147483647;
- files = (
- 8280E8D615C7303000CDF59C /* main.m in Sources */,
- );
- runOnlyForDeploymentPostprocessing = 0;
- };
-/* End PBXSourcesBuildPhase section */
-
-/* Begin XCBuildConfiguration section */
- 82DC012515C6EE760098E42A /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
- CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- COPY_PHASE_STRIP = NO;
- GCC_C_LANGUAGE_STANDARD = gnu99;
- GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_OBJC_EXCEPTIONS = YES;
- GCC_OPTIMIZATION_LEVEL = 0;
- GCC_PREPROCESSOR_DEFINITIONS = (
- "DEBUG=1",
- "$(inherited)",
- );
- GCC_SYMBOLS_PRIVATE_EXTERN = NO;
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES;
- GCC_WARN_UNINITIALIZED_AUTOS = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- MACOSX_DEPLOYMENT_TARGET = 10.7;
- ONLY_ACTIVE_ARCH = YES;
- SDKROOT = macosx10.7;
- };
- name = Debug;
- };
- 82DC012615C6EE760098E42A /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
- CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
- CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- COPY_PHASE_STRIP = YES;
- DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
- GCC_C_LANGUAGE_STANDARD = gnu99;
- GCC_ENABLE_OBJC_EXCEPTIONS = YES;
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES;
- GCC_WARN_UNINITIALIZED_AUTOS = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- MACOSX_DEPLOYMENT_TARGET = 10.7;
- SDKROOT = macosx10.7;
- };
- name = Release;
- };
- 82DC012815C6EE760098E42A /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
- GCC_PRECOMPILE_PREFIX_HEADER = YES;
- GCC_PREFIX_HEADER = "";
- ONLY_ACTIVE_ARCH = NO;
- PRODUCT_NAME = "$(TARGET_NAME)";
- SDKROOT = macosx10.7;
- };
- name = Debug;
- };
- 82DC012915C6EE760098E42A /* Release */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
- GCC_PRECOMPILE_PREFIX_HEADER = YES;
- GCC_PREFIX_HEADER = "";
- ONLY_ACTIVE_ARCH = NO;
- PRODUCT_NAME = "$(TARGET_NAME)";
- SDKROOT = macosx10.7;
- };
- name = Release;
- };
-/* End XCBuildConfiguration section */
-
-/* Begin XCConfigurationList section */
- 82DC011215C6EE760098E42A /* Build configuration list for PBXProject "fsevents-wrapper" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- 82DC012515C6EE760098E42A /* Debug */,
- 82DC012615C6EE760098E42A /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
- 82DC012715C6EE760098E42A /* Build configuration list for PBXNativeTarget "fsevents-wrapper" */ = {
- isa = XCConfigurationList;
- buildConfigurations = (
- 82DC012815C6EE760098E42A /* Debug */,
- 82DC012915C6EE760098E42A /* Release */,
- );
- defaultConfigurationIsVisible = 0;
- defaultConfigurationName = Release;
- };
-/* End XCConfigurationList section */
- };
- rootObject = 82DC010F15C6EE760098E42A /* Project object */;
-}
View
7 ext/fsevents-wrapper.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Workspace
- version = "1.0">
- <FileRef
- location = "self:fsevents-wrapper.xcodeproj">
- </FileRef>
-</Workspace>
View
BIN  ...events-wrapper.xcodeproj/project.xcworkspace/xcuserdata/burke.xcuserdatad/UserInterfaceState.xcuserstate
Binary file not shown
View
5 ext/fsevents-wrapper.xcodeproj/xcuserdata/burke.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Bucket
- type = "1"
- version = "1.0">
-</Bucket>
View
86 ext/fsevents-wrapper.xcodeproj/xcuserdata/burke.xcuserdatad/xcschemes/fsevents-wrapper 2.xcscheme
@@ -1,86 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
- LastUpgradeVersion = "0440"
- version = "1.3">
- <BuildAction
- parallelizeBuildables = "YES"
- buildImplicitDependencies = "YES">
- <BuildActionEntries>
- <BuildActionEntry
- buildForTesting = "YES"
- buildForRunning = "YES"
- buildForProfiling = "YES"
- buildForArchiving = "YES"
- buildForAnalyzing = "YES">
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "82DC011715C6EE760098E42A"
- BuildableName = "fsevents-wrapper"
- BlueprintName = "fsevents-wrapper"
- ReferencedContainer = "container:fsevents-wrapper.xcodeproj">
- </BuildableReference>
- </BuildActionEntry>
- </BuildActionEntries>
- </BuildAction>
- <TestAction
- selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
- selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- shouldUseLaunchSchemeArgsEnv = "YES"
- buildConfiguration = "Debug">
- <Testables>
- </Testables>
- <MacroExpansion>
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "82DC011715C6EE760098E42A"
- BuildableName = "fsevents-wrapper"
- BlueprintName = "fsevents-wrapper"
- ReferencedContainer = "container:fsevents-wrapper.xcodeproj">
- </BuildableReference>
- </MacroExpansion>
- </TestAction>
- <LaunchAction
- selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
- selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- launchStyle = "0"
- useCustomWorkingDirectory = "NO"
- buildConfiguration = "Debug"
- ignoresPersistentStateOnLaunch = "NO"
- debugDocumentVersioning = "YES"
- allowLocationSimulation = "YES">
- <BuildableProductRunnable>
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "82DC011715C6EE760098E42A"
- BuildableName = "fsevents-wrapper"
- BlueprintName = "fsevents-wrapper"
- ReferencedContainer = "container:fsevents-wrapper.xcodeproj">
- </BuildableReference>
- </BuildableProductRunnable>
- <AdditionalOptions>
- </AdditionalOptions>
- </LaunchAction>
- <ProfileAction
- shouldUseLaunchSchemeArgsEnv = "YES"
- savedToolIdentifier = ""
- useCustomWorkingDirectory = "NO"
- buildConfiguration = "Release"
- debugDocumentVersioning = "YES">
- <BuildableProductRunnable>
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "82DC011715C6EE760098E42A"
- BuildableName = "fsevents-wrapper"
- BlueprintName = "fsevents-wrapper"
- ReferencedContainer = "container:fsevents-wrapper.xcodeproj">
- </BuildableReference>
- </BuildableProductRunnable>
- </ProfileAction>
- <AnalyzeAction
- buildConfiguration = "Debug">
- </AnalyzeAction>
- <ArchiveAction
- buildConfiguration = "Release"
- revealArchiveInOrganizer = "YES">
- </ArchiveAction>
-</Scheme>
View
86 ext/fsevents-wrapper.xcodeproj/xcuserdata/burke.xcuserdatad/xcschemes/fsevents-wrapper.xcscheme
@@ -1,86 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
- LastUpgradeVersion = "0440"
- version = "1.3">
- <BuildAction
- parallelizeBuildables = "YES"
- buildImplicitDependencies = "YES">
- <BuildActionEntries>
- <BuildActionEntry
- buildForTesting = "YES"
- buildForRunning = "YES"
- buildForProfiling = "YES"
- buildForArchiving = "YES"
- buildForAnalyzing = "YES">
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "82DC011715C6EE760098E42A"
- BuildableName = "fsevents-wrapper"
- BlueprintName = "fsevents-wrapper"
- ReferencedContainer = "container:fsevents-wrapper.xcodeproj">
- </BuildableReference>
- </BuildActionEntry>
- </BuildActionEntries>
- </BuildAction>
- <TestAction
- selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
- selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- shouldUseLaunchSchemeArgsEnv = "YES"
- buildConfiguration = "Debug">
- <Testables>
- </Testables>
- <MacroExpansion>
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "82DC011715C6EE760098E42A"
- BuildableName = "fsevents-wrapper"
- BlueprintName = "fsevents-wrapper"
- ReferencedContainer = "container:fsevents-wrapper.xcodeproj">
- </BuildableReference>
- </MacroExpansion>
- </TestAction>
- <LaunchAction
- selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
- selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- launchStyle = "0"
- useCustomWorkingDirectory = "NO"
- buildConfiguration = "Debug"
- ignoresPersistentStateOnLaunch = "NO"
- debugDocumentVersioning = "YES"
- allowLocationSimulation = "YES">
- <BuildableProductRunnable>
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "82DC011715C6EE760098E42A"
- BuildableName = "fsevents-wrapper"
- BlueprintName = "fsevents-wrapper"
- ReferencedContainer = "container:fsevents-wrapper.xcodeproj">
- </BuildableReference>
- </BuildableProductRunnable>
- <AdditionalOptions>
- </AdditionalOptions>
- </LaunchAction>
- <ProfileAction
- shouldUseLaunchSchemeArgsEnv = "YES"
- savedToolIdentifier = ""
- useCustomWorkingDirectory = "NO"
- buildConfiguration = "Release"
- debugDocumentVersioning = "YES">
- <BuildableProductRunnable>
- <BuildableReference
- BuildableIdentifier = "primary"
- BlueprintIdentifier = "82DC011715C6EE760098E42A"
- BuildableName = "fsevents-wrapper"
- BlueprintName = "fsevents-wrapper"
- ReferencedContainer = "container:fsevents-wrapper.xcodeproj">
- </BuildableReference>
- </BuildableProductRunnable>
- </ProfileAction>
- <AnalyzeAction
- buildConfiguration = "Debug">
- </AnalyzeAction>
- <ArchiveAction
- buildConfiguration = "Release"
- revealArchiveInOrganizer = "YES">
- </ArchiveAction>
-</Scheme>
View
27 ext/fsevents-wrapper.xcodeproj/xcuserdata/burke.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>SchemeUserState</key>
- <dict>
- <key>fsevents-wrapper 2.xcscheme</key>
- <dict>
- <key>orderHint</key>
- <integer>1</integer>
- </dict>
- <key>fsevents-wrapper.xcscheme</key>
- <dict>
- <key>orderHint</key>
- <integer>0</integer>
- </dict>
- </dict>
- <key>SuppressBuildableAutocreation</key>
- <dict>
- <key>82DC011715C6EE760098E42A</key>
- <dict>
- <key>primary</key>
- <true/>
- </dict>
- </dict>
-</dict>
-</plist>
View
BIN  ext/fsevents-wrapper/fsevents-wrapper
Binary file not shown
View
118 ext/fsevents-wrapper/main.m
@@ -1,118 +0,0 @@
-#import <Foundation/Foundation.h>
-#include <CoreServices/CoreServices.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-
-static CFMutableArrayRef _watchedFiles;
-static FSEventStreamRef _activeStream;
-static NSMutableDictionary *_fileIsWatched;
-
-static int flagsWorthReloadingFor = \
-kFSEventStreamEventFlagItemRemoved | \
-kFSEventStreamEventFlagItemRenamed | \
-kFSEventStreamEventFlagItemModified;
-
-void myCallbackFunction(
- ConstFSEventStreamRef streamRef,
- void *clientCallBackInfo,
- size_t numEvents,
- void *eventPaths,
- const FSEventStreamEventFlags eventFlags[],
- const FSEventStreamEventId eventIds[])
-{
- int i, flags;
- char **paths = eventPaths;
-
- for (i = 0; i < numEvents; i++) {
- flags = eventFlags[i];
-
- if (flags & (kFSEventStreamEventFlagItemIsFile | flagsWorthReloadingFor)) {
- printf("%s\n", paths[i]);
- fflush(stdout);
- }
- }
-}
-
-void configureStream()
-{
- if (CFArrayGetCount(_watchedFiles) == 0) return;
-
- if (_activeStream) {
- FSEventStreamStop(_activeStream);
- FSEventStreamUnscheduleFromRunLoop(_activeStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
- //CFRelease(_activeStream);
- }
-
- _activeStream = FSEventStreamCreate(NULL,
- &myCallbackFunction,
- NULL,
- _watchedFiles,
- kFSEventStreamEventIdSinceNow,
- 1.0, // latency
- kFSEventStreamCreateFlagFileEvents);
-
- FSEventStreamScheduleWithRunLoop(_activeStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
-
- FSEventStreamStart(_activeStream);
-
-}
-
-int maybeAddFileToWatchList(char *line)
-{
- CFStringRef file = CFStringCreateWithCString(NULL, line, kCFStringEncodingASCII);
- struct stat buf;
-
- if ([_fileIsWatched valueForKey:(__bridge NSString *)file]) {
- return 0;
- } else if (stat(line, &buf) == 0) {
- [_fileIsWatched setValue:@"yes" forKey:(__bridge NSString *)file];
- CFArrayAppendValue(_watchedFiles, file);
- return 1;
- } else {
- return 0;
- }
-}
-
-void handleInputFiles()
-{
- int anyChanges = 0;
-
- char line[2048];
-
- while (fgets(line, sizeof(line), stdin) != NULL) {
- line[strlen(line)-1] = 0;
- anyChanges |= maybeAddFileToWatchList(line);
- }
-
- if (anyChanges) {
- configureStream();
- }
-}
-
-void configureTimerAndRun()
-{
- CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL,
- 0,
- 0.5,
- 0,
- 0,
- &handleInputFiles,
- NULL);
-
- CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
- CFRunLoopRun();
-}
-
-int main(int argc, const char * argv[])
-{
- int flags = fcntl(0, F_GETFL);
- flags |= O_NONBLOCK;
- fcntl(STDIN_FILENO, F_SETFL, flags);
-
- _watchedFiles = CFArrayCreateMutable(NULL, 0, NULL);
- _fileIsWatched = [[NSMutableDictionary alloc] initWithCapacity:500];
-
- configureTimerAndRun();
- return 0;
-}
View
97 lib/thrud.rb
@@ -1,97 +0,0 @@
-class Thrud
-
- class Task < Struct.new(:method_name, :desc, :long_desc, :method_options)
- def arity(obj)
- obj.method(method_name).arity
- end
- end
-
- def self.desc(name, a)
- @desc = a
- end
- def self.long_desc(a)
- @long_desc = a
- end
- def self.method_option(*a)
- @method_options ||= []
- @method_options << a
- end
-
- def self.method_added(m)
- desc, long_desc, method_options = @desc, @long_desc, (@method_options||[])
- @desc, @long_desc, @method_options = nil, nil, nil
-
- @tasks ||= {}
- @tasks[m.to_s] = Task.new(m, desc, long_desc, method_options)
- end
-
- def self.map(a)
- a.each do |aliases, target|
- aliases = [aliases] unless aliases.kind_of?(Array)
- aliases.each do |name|
- @tasks[name.to_s] = @tasks[target.to_s]
- end
- end
- end
-
- def self.task_for_name(name)
- @tasks[name.to_s]
- end
-
- def task_for_name(name)
- self.class.task_for_name(name)
- end
-
- def help(taskname = nil)
- if taskname && task = task_for_name(taskname)
- arity = task.arity(self)
- Zeus.ui.info <<-BANNER
-Usage:
- zeus #{taskname} #{arity == -1 ? "[ARGS]" : ""}
-
-Description:
- #{task.long_desc || task.desc}
-BANNER
- else
- # this is super non-generic. problem for future-burke.
- project_tasks = self.class.instance_variable_get("@tasks").
- reject{|k,v|['definition_file', 'initialize', 'version', 'init', 'start', 'help'].include?(v.method_name.to_s)}.values.uniq
-
- tasks = project_tasks.map { |task|
- " zeus %-14s # %s" % [task.method_name, task.desc]
- }
-
- Zeus.ui.info <<-BANNER
-Global Commands:
- zeus help # show this help menu
- zeus help [COMMAND] # show help for a specific command
- zeus init # #{task_for_name(:init).desc}
- zeus start # #{task_for_name(:start).desc}
- zeus version # #{task_for_name(:version).desc}
-
-Project-local Commands:
-#{tasks.join("\n")}
-BANNER
- end
- end
-
- def self.start
- taskname = ARGV.shift
- arguments = ARGV
-
- taskname ||= "help"
-
- unless task = @tasks[taskname.to_s]
- Zeus.ui.error "Could not find task \"#{taskname}\""
- exit 1
- end
-
- instance = new
- if instance.method(task.method_name).arity == 0 && arguments.any?
- Zeus.ui.error "\"#{task.method_name}\" was called incorrectly. Call as \"zeus #{task.method_name}\"."
- exit 1
- end
- instance.send(task.method_name, *arguments)
- end
-
-end
View
47 lib/zeus.rb
@@ -1,47 +0,0 @@
-require 'zeus/version'
-require 'zeus/ui'
-require 'zeus/plan'
-require 'zeus/server'
-require 'zeus/client'
-require 'zeus/error_printer'
-require 'zeus/cli'
-
-module Zeus
- SOCKET_NAME = '.zeus.sock'
-
- class ZeusError < StandardError
- def self.status_code(code)
- define_method(:status_code) { code }
- end
- end
-
- def self.ui
- @ui ||= UI.new
- end
-
- def self.ui=(ui)
- @ui = ui
- end
-
- def self.after_fork(&b)
- @after_fork ||= []
- @after_fork << b
- end
-
- def self.run_after_fork!
- @after_fork.map(&:call) if @after_fork
- @after_fork = []
- end
-
- def self.thread_with_backtrace_on_error(&b)
- Thread.new {
- begin
- b.call
- rescue => e
- ErrorPrinter.new(e).write_to($stdout)
- end
- }
- end
-
-end
-
View
80 lib/zeus/cli.rb
@@ -1,80 +0,0 @@
-require 'thrud'
-
-module Zeus
- class CLI < Thrud
-
- def initialize(*)
- super
- Zeus.ui = Zeus::UI.new
- Zeus.ui.debug! #if options['verbose']
- end
-
- desc "init", "Generates a zeus config file in the current working directory"
- long_desc <<-D
- Init tries to determine what kind of project is in the current working directory,
- and generates a relevant config file. Currently the only supported template is
- rails.
- D
- # method_option "rails", type: :string, banner: "Use the rails template instead of auto-detecting based on project contents"
- def init
- require 'fileutils'
-
- if File.exist?(".zeus.rb")
- Zeus.ui.error ".zeus.rb already exists at #{Dir.pwd}/.zeus.rb"
- exit 1
- end
-
- Zeus.ui.info "Writing new .zeus.rb to #{Dir.pwd}/.zeus.rb"
- FileUtils.cp(File.expand_path("../templates/rails.rb", __FILE__), '.zeus.rb')
- end
-
- desc "start", "Start a zeus server for the project in the current working directory"
- long_desc <<-D
- starts a server that boots your application using the config file in
- .zeus.rb. The server will take several seconds to start, after which you may
- use the zeus runner commands (see `zeus help` for a list of available commands).
- D
- def start
- begin
- require self.class.definition_file
- rescue LoadError
- Zeus.ui.error("Your project is missing a config file (.zeus.rb), and it doesn't appear\n"\
- "to be a rails project. You can run `zeus init` to generate a config file.")
- exit 1
- end
- Zeus::Server.new.run
- end
-
- def help(*)
- super
- end
-
- desc "version", "Print zeus's version information and exit"
- def version
- Zeus.ui.info "Zeus version #{Zeus::VERSION}"
- end
- map %w(-v --version) => :version
-
- def self.definition_file
- if !File.exists?('.zeus.rb') && File.exists?('script/rails')
- File.expand_path("../templates/rails.rb", __FILE__)
- else
- './.zeus.rb'
- end
- end
-
- begin
- require definition_file
- rescue LoadError
- end
-
- Zeus::Server.acceptors.each do |acc|
- desc acc.name, (acc.description || "#{acc.name} task defined in zeus definition file")
- define_method(acc.name) { |*args|
- Zeus::Client.run(acc.name, args)
- }
- map acc.aliases => acc.name
- end
-
- end
-end
View
114 lib/zeus/client.rb
@@ -1,114 +0,0 @@
-# encoding: utf-8
-begin
- require "io/console"
-rescue LoadError
- Zeus.ui.error "io/console not found. Please `gem install io-console` or, preferably, " +
- "install ruby 1.9.3 by following the instructions at: " +
- "https://gist.github.com/1688857"
- exit 1
-end
-require "json"
-require "pty"
-require "socket"
-
-require 'zeus/client/winsize'
-
-module Zeus
- class Client
- include Winsize
-
- attr_accessor :pid
-
- SIGNALS = {
- "\x03" => "INT",
- "\x1C" => "QUIT",
- "\x1A" => "TSTP",
- }
- SIGNAL_REGEX = Regexp.union(SIGNALS.keys)
-
- def self.run(command, args)
- new.run(command, args)
- end
-
- def run(command, args)
- maybe_raw do
- PTY.open do |master, slave|
- @exit_status, @es2 = IO.pipe
- @master = master
- set_winsize
- make_winch_channel
- @pid = connect_to_server(command, args, slave)
-
- select_loop!
- end
- end
- end
-
- private
-
- def select_loop!
- buffer = ""
- while ready = select([winch, @master, $stdin, @exit_status])[0]
- handle_winch if ready.include?(winch)
- handle_stdin(buffer) if ready.include?($stdin)
- handle_master(buffer) if ready.include?(@master)
- handle_exit if ready.include?(@exit_status)
- end
- rescue EOFError
- end
-
- def handle_exit
- exit @exit_status.readline.chomp.to_i
- end
-
- def connect_to_server(command, arguments, slave, socket_path = Zeus::SOCKET_NAME)
- socket = UNIXSocket.new(socket_path)
- socket << {command: command, arguments: arguments}.to_json << "\n"
- socket.send_io(slave)
- socket.send_io(@es2)
- slave.close
-
- pid = socket.readline.chomp.to_i
- trap("CONT") { Process.kill("CONT", @pid) }
- pid
- rescue Errno::ENOENT, Errno::ECONNREFUSED, Errno::ECONNRESET
- # we need a \r at the end because the terminal is in raw mode.
- Zeus.ui.error "Zeus doesn't seem to be running, try 'zeus start`\r"
- exit 1
- end
-
- def handle_stdin(buffer)
- input = $stdin.readpartial(4096, buffer)
- input.scan(SIGNAL_REGEX).each { |signal|
- begin
- send_signal(signal, pid)
- rescue Errno::ESRCH
- exit # the remote process died. Just quit.
- end
- }
- @master << input
- end
-
- def send_signal(signal, pid)
- if SIGNALS[signal] == "TSTP"
- Process.kill("STOP", pid)
- Process.kill("TSTP", Process.pid)
- else
- Process.kill(SIGNALS[signal], pid)
- end
- end
-
- def handle_master(buffer)
- $stdout << @master.readpartial(4096, buffer)
- end
-
- def maybe_raw(&b)
- if $stdout.tty?
- $stdout.raw(&b)
- else
- b.call
- end
- end
-
- end
-end
View
28 lib/zeus/client/winsize.rb
@@ -1,28 +0,0 @@
-module Zeus
- class Client
- module Winsize
-
- attr_reader :winch
-
- def set_winsize
- $stdout.tty? and @master.winsize = $stdout.winsize
- end
-
- def make_winch_channel
- @winch, winch_ = IO.pipe
- trap("WINCH") { winch_ << "\0" }
- end
-
- def handle_winch
- @winch.read(1)
- set_winsize
- begin
- Process.kill("WINCH", pid) if pid
- rescue Errno::ESRCH
- exit # the remote process died. Just quit.
- end
- end
-
- end
- end
-end
View
16 lib/zeus/error_printer.rb
@@ -1,16 +0,0 @@
-module Zeus
- class ErrorPrinter
- attr_reader :error
- def initialize(error)
- @error = error
- end
-
- def write_to(io)
- io.puts "#{error.backtrace[0]}: #{error.message} (#{error.class})"
- error.backtrace[1..-1].each do |line|
- io.puts "\tfrom #{line}"
- end
- end
-
- end
-end
View
18 lib/zeus/plan.rb
@@ -1,18 +0,0 @@
-require 'set'
-
-require 'zeus/plan/node'
-require 'zeus/plan/stage'
-require 'zeus/plan/acceptor'
-
-module Zeus
- module Plan
- class Evaluator
- def stage(name, &b)
- stage = Plan::Stage.new(name)
- stage.root = true
- stage.instance_eval(&b)
- stage
- end
- end
- end
-end
View
38 lib/zeus/plan/acceptor.rb
@@ -1,38 +0,0 @@
-module Zeus
- module Plan
-
- class Acceptor < Node
-
- attr_reader :name, :aliases, :description, :action
- def initialize(name, aliases, description, &b)
- super(name)
- @description = description
- @aliases = aliases
- @action = b
- end
-
- # ^ configuration
- # V later use
-
- def commands
- [name, *aliases].map(&:to_s)
- end
-
- def acceptors
- self
- end
-
- def to_process_object(server)
- Zeus::Server::Acceptor.new(server).tap do |stage|
- stage.name = @name
- stage.aliases = @aliases
- stage.action = @action
- stage.description = @description
- end
- end
-
- end
-
- end
-end
-
View
66 lib/zeus/plan/node.rb
@@ -1,66 +0,0 @@
-module Zeus
- module Plan
-
- class Node
- attr_reader :name, :stages, :features
- attr_accessor :pid, :root
-
- def initialize(name)
- @name = name
- @stages = []
- @features = Set.new # hash might be faster than ruby's inane impl of set.
- end
-
- def stage_has_feature(name, file)
- node_for_name(name).features << file
- end
-
- def stage_has_pid(name, pid)
- node_for_name(name).pid = pid
- end
-
- def kill_nodes_with_feature(file)
- if features.include?(file)
- if root
- Zeus.ui.error "One of zeus's dependencies changed. Not killing zeus. You may have to restart the server."
- else
- kill!
- end
- else
- stages.each do |child|
- child.kill_nodes_with_feature(file)
- end
- end
- end
-
- # We send STOP before actually killing the processes here.
- # This is to prevent parents from respawning before all the children
- # are killed. This prevents a race condition.
- def kill!
- Process.kill("STOP", pid) if pid
- # Protected methods don't work with each(&:m) notation.
- stages.each { |stage| stage.kill! }
- old_pid = pid
- self.pid = nil
- Process.kill("KILL", old_pid) if old_pid
- end
-
- private
-
- def node_for_name(name)
- @nodes_by_name ||= __nodes_by_name
- @nodes_by_name[name]
- end
-
- def __nodes_by_name
- nodes = {name => self}
- stages.each do |child|
- nodes.merge!(child.__nodes_by_name)
- end
- nodes
- end ; protected :__nodes_by_name
-
- end
-
- end
-end
View
50 lib/zeus/plan/stage.rb
@@ -1,50 +0,0 @@
-module Zeus
- module Plan
-
- class Stage < Node
-
- attr_reader :actions
- def initialize(name)
- super(name)
- @actions = []
- end
-
- def action(&b)
- @actions << b
- self
- end
-
- def desc(desc)
- @desc = desc
- end
-
- def stage(name, &b)
- @stages << Plan::Stage.new(name).tap { |s| s.instance_eval(&b) }
- self
- end
-
- def command(name, *aliases, &b)
- @stages << Plan::Acceptor.new(name, aliases, @desc, &b)
- @desc = nil
- self
- end
-
- # ^ configuration
- # V later use
-
- def acceptors
- stages.map(&:acceptors).flatten
- end
-
- def to_process_object(server)
- Zeus::Server::Stage.new(server).tap do |stage|
- stage.name = @name
- stage.stages = @stages.map { |stage| stage.to_process_object(server) }
- stage.actions = @actions
- end
- end
-
- end
-
- end
-end
View
104 lib/zeus/server.rb
@@ -1,104 +0,0 @@
-require 'json'
-require 'socket'
-require 'forwardable'
-
-require 'zeus/server/stage'
-require 'zeus/server/acceptor'
-require 'zeus/server/file_monitor'
-require 'zeus/server/at_exit_hack'
-require 'zeus/server/load_tracking'
-require 'zeus/server/client_handler'
-require 'zeus/server/command_runner'
-require 'zeus/server/process_tree_monitor'
-require 'zeus/server/acceptor_registration_monitor'
-
-module Zeus
- class Server
- extend Forwardable
-
- def self.define!(&b)
- @@definition = Zeus::Plan::Evaluator.new.instance_eval(&b)
- end
-
- def self.acceptors
- defined?(@@definition) ? @@definition.acceptors : []
- end
-
- def initialize
- @file_monitor = FileMonitor::FSEvent.new(&method(:dependency_did_change))
- @acceptor_registration_monitor = AcceptorRegistrationMonitor.new
- @process_tree_monitor = ProcessTreeMonitor.new(@file_monitor, @@definition)
- @client_handler = ClientHandler.new(acceptor_commands, self)
-
- @plan = @@definition.to_process_object(self)
- end
-
- def dependency_did_change(file)
- @process_tree_monitor.kill_nodes_with_feature(file)
- end
-
- def monitors
- [@file_monitor, @process_tree_monitor, @acceptor_registration_monitor, @client_handler]
- end
-
- def run
- $0 = "zeus master"
- trap("TERM") { exit 0 }
- trap("INT") { puts "\n\x1b[31mExiting\x1b[0m" ; exit }
- LoadTracking.server = self
-
- @plan.run(true) # boot the actual app
- master = Process.pid
- at_exit { cleanup_all_children if Process.pid == master }
- monitors.each(&:close_child_socket)
-
- runloop!
- ensure
- File.unlink(Zeus::SOCKET_NAME)
- end
-
- def cleanup_all_children
- @process_tree_monitor.kill_all_nodes
- @file_monitor.kill_wrapper
- end
-
- def add_extra_feature(full_expanded_path)
- $extra_loaded_features ||= []
- $extra_loaded_features << full_expanded_path
- end
-
- def extra_features
- $extra_loaded_features || []
- end
-
- # Child process API
- def __CHILD__close_parent_sockets
- monitors.each(&:close_parent_socket)
- end
-
- def_delegators :@acceptor_registration_monitor,
- :__CHILD__register_acceptor,
- :__CHILD__find_acceptor_for_command
-
- def_delegators :@process_tree_monitor,
- :__CHILD__stage_starting_with_pid,
- :__CHILD__stage_has_feature
-
- private
-
- def acceptor_commands
- self.class.acceptors.map(&:commands).flatten
- end
-
- def runloop!
- loop do
- ready, = IO.select(monitors.map(&:datasource), [], [], 1)
- next unless ready
- monitors.each do |m|
- m.on_datasource_event if ready.include?(m.datasource)
- end
- end
- end
-
- end
-end
View
82 lib/zeus/server/acceptor.rb
@@ -1,82 +0,0 @@
-require 'json'
-require 'socket'
-
-module Zeus
- class Server
- class Acceptor
- attr_accessor :aliases, :description, :action, :name
- def initialize(server)
- @server = server
- end
-
- def descendent_acceptors
- self
- end
-
- def run
- register_with_client_handler(Process.pid)
- Zeus.ui.info("starting #{process_type} `#{@name}`")
- Zeus.thread_with_backtrace_on_error { runloop! }
- end
-
- private
-
- def command_runner
- CommandRunner.new(name, action, @s_acceptor)
- end
-
- def register_with_client_handler(pid)
- @s_client_handler, @s_acceptor = UNIXSocket.pair
- @s_acceptor.puts registration_data(pid)
- at_exit { @s_client_handler.close ; @s_acceptor.close }
- @server.__CHILD__register_acceptor(@s_client_handler)
- end
-
- def registration_data(pid)
- {type: 'registration', pid: pid, commands: [name, *aliases], description: description}.to_json
- end
-
- def accept_connection
- terminal = @s_acceptor.recv_io # blocking
- exit_status_socket = @s_acceptor.recv_io
- arguments = JSON.parse(@s_acceptor.readline.chomp)
-
- [terminal, exit_status_socket, arguments]
- rescue IOError, Errno::EBADF
- sleep 0.2
- retry
- end
-
- def process_type
- "acceptor"
- end
-
- def runloop!
- loop do
- terminal, exit_status_socket, arguments = accept_connection # blocking
- command_runner.run(terminal, exit_status_socket, arguments)
- end
- end
-
- module ErrorState
- NOT_A_PID = 0
- attr_accessor :error
-
- def process_type
- "error-state acceptor"
- end
-
- def runloop!
- loop do
- terminal = @s_acceptor.recv_io
- _ = @s_acceptor.readline
- @s_acceptor << NOT_A_PID << "\n"
- ErrorPrinter.new(@error).write_to(terminal)
- terminal.close
- end
- end
-
- end
- end
- end
-end
View
75 lib/zeus/server/acceptor_registration_monitor.rb
@@ -1,75 +0,0 @@
-module Zeus
- class Server
- class AcceptorRegistrationMonitor
-
- def datasource ; @sock ; end
- def on_datasource_event ; handle_message ; end
- # @__CHILD__sock is not closed here, as it's used by the master to respond
- # on behalf of unbooted acceptors
- def close_child_socket ; end
- def close_parent_socket ; @sock.close ; end
-
- def initialize
- @sock, @__CHILD__sock = UNIXSocket.pair
- @acceptors = []
- @pings = {}
- end
-
- AcceptorStub = Struct.new(:pid, :socket, :commands, :description)
-
- def handle_message
- io = @sock.recv_io
-
- data = JSON.parse(io.readline.chomp)
- type = data['type']
-
- case type
- when 'wait' ; handle_wait(io, data)
- when 'registration' ; handle_registration(io, data)
- else raise "invalid message"
- end
- end
-
- def handle_wait(io, data)
- command = data['command'].to_s
- @pings[command] ||= []
- @pings[command] << io
- end
-
- def handle_registration(io, data)
- pid = data['pid'].to_i
- commands = data['commands']
- description = data['description']
-
- @acceptors.reject!{|ac|ac.commands == commands}
- @acceptors << AcceptorStub.new(pid, io, commands, description)
- notify_pings_for_commands(commands)
- end
-
- def notify_pings_for_commands(commands)
- (commands || []).each do |command|
- (@pings[command.to_s] || []).each do |ping|
- ping.puts "ready\n"
- ping.close
- end
- @pings[command.to_s] = nil
- end
- end
-
- module ChildProcessApi
-
- def __CHILD__find_acceptor_for_command(command)
- @acceptors.detect { |acceptor|
- acceptor.commands.include?(command)
- }
- end
-
- def __CHILD__register_acceptor(io)
- @__CHILD__sock.send_io(io)
- end
-
- end ; include ChildProcessApi
-
- end
- end
-end
View
36 lib/zeus/server/at_exit_hack.rb
@@ -1,36 +0,0 @@
-module Kernel
-
- alias_method :prepend_at_exit, :at_exit
- if ENV['EXIT_DEBUG']
- def at_exit(&block)
- puts "\x1b[36mRegistering block using at_exit in #{caller[0]}\x1b[0m"
- blk = caller[0]
- prepend_at_exit {
- print "\x1b[36mRunning block using at_exit in #{blk}...\x1b[0m" ; $stdout.flush
- block.call
- puts "\x1b[36mdone!\x1b[0m"
- }
- end
- end
-
- def append_at_exit(&block)
- ENV['EXIT_DEBUG'] and puts "\x1b[36mRegistering block using \x1b[32mappend\x1b[36m in #{caller[0]}\x1b[0m"
- @@at_exit_blocks ||= []
- @@at_exit_blocks << block
- end
-
- at_exit {
- ENV['EXIT_DEBUG'] and puts "\x1b[36mRunning #{at_exit_blocks.size} at_exit_blocks\x1b[0m"
- at_exit_blocks.each do |b|
- ENV['EXIT_DEBUG'] and puts "\x1b[36mRunning \x1b[32mappended\x1b[36m block #{b.inspect}\x1b[0m"
- b.call
- end
- }
-
- private
-
- def at_exit_blocks
- defined?(@@at_exit_blocks) ? @@at_exit_blocks : []
- end
-
-end
View
106 lib/zeus/server/client_handler.rb
@@ -1,106 +0,0 @@
-require 'socket'
-require 'json'
-
-# This class is a little confusing. See the docs/ directory for guidance.
-module Zeus
- class Server
- class ClientHandler
- def datasource ; @listener ; end
- def on_datasource_event ; handle_server_connection ; end
- def close_child_socket ; end
- def close_parent_socket ; @listener.close ; end
-
- REATTEMPT_HANDSHAKE = 204
-
- def initialize(acceptor_commands, server)
- @server = server
- @acceptor_commands = acceptor_commands
- @listener = UNIXServer.new(Zeus::SOCKET_NAME)
- @listener.listen(10)
- rescue Errno::EADDRINUSE
- Zeus.ui.error "Zeus appears to be already running in this project. If not, remove #{Zeus::SOCKET_NAME} and try again."
- exit 1
- end
-
- private
-
- # See docs/client_server_handshake.md for details
- def handle_server_connection
- s_client = @listener.accept
-
- data = JSON.parse(s_client.readline.chomp)
- command, arguments = data.values_at('command', 'arguments')
-
- client_terminal = s_client.recv_io
- exit_status_socket = s_client.recv_io
-
- Thread.new {
- # This is a little ugly. Gist: Try to handshake the client to the acceptor.
- # If the acceptor is not booted yet, this will hang until it is, then terminate with
- # REATTEMPT_HANDSHAKE. We catch that exit code and try once more.
- begin
- loop do
- pid = fork { handshake_client_to_acceptor(s_client, command, arguments, client_terminal, exit_status_socket) ; exit }
- Process.wait(pid)
- break if $?.exitstatus != REATTEMPT_HANDSHAKE
- end
- ensure
- client_terminal.close
- s_client.close
- end
- }
- end
-
- def handshake_client_to_acceptor(s_client, command, arguments, client_terminal, exit_status_socket)
- unless @acceptor_commands.include?(command.to_s)
- msg = "no such command `#{command}`."
- return exit_with_message(s_client, client_terminal, msg)
- end
-
- unless acceptor = send_io_to_acceptor(client_terminal, exit_status_socket, command)
- wait_for_acceptor(s_client, client_terminal, command)
- exit REATTEMPT_HANDSHAKE
- end
-
- Zeus.ui.info "accepting connection for #{command}"
-
- acceptor.socket.puts arguments.to_json
- pid = acceptor.socket.readline.chomp.to_i
- s_client.puts pid
- s_client.close
- end
-
- def exit_with_message(s_client, client_terminal, msg)
- s_client << "0\n"
- client_terminal << "[zeus] #{msg}\n"
- client_terminal.close
- s_client.close
- exit 1
- end
-
- def wait_for_acceptor(s_client, client_terminal, command)
- s_client << "0\n"
- client_terminal << "[zeus] waiting for `#{command}` to finish booting...\n"
-
- s, r = UNIXSocket.pair
- s << {type: 'wait', command: command}.to_json << "\n"
- @server.__CHILD__register_acceptor(r)
-
- s.readline # wait until acceptor is booted
- end
-
- def send_io_to_acceptor(io, io2, command)
- return false unless acceptor = @server.__CHILD__find_acceptor_for_command(command)
- return false unless usock = UNIXSocket.for_fd(acceptor.socket.fileno)
- usock.send_io(io)
- usock.send_io(io2)
- io.close
- io2.close
- return acceptor
- rescue Errno::EPIPE
- return false
- end
-
- end
- end
-end
View
84 lib/zeus/server/command_runner.rb
@@ -1,84 +0,0 @@
-module Zeus
- class Server
- class CommandRunner
-
- def initialize(name, action, s_acceptor)
- @name = name
- @action = action
- @s_acceptor = s_acceptor
- end
-
- def run(terminal, exit_status_socket, arguments)
- child = fork { _run(terminal, exit_status_socket, arguments) }
- terminal.close
- exit_status_socket.close
- Process.detach(child)
- child
- end
-
- private
-
- def _run(terminal, exit_status_socket, arguments)
- $0 = "zeus runner: #{@name}"
- @exit_status_socket = exit_status_socket
- @terminal = terminal
- Process.setsid
- reconnect_things!
- @s_acceptor << $$ << "\n"
- reopen_streams(terminal, terminal, terminal)
- ARGV.replace(arguments)
-
- return_process_exit_status
-
- run_action
- end
-
- def return_process_exit_status
- append_at_exit do
- if $!.nil? || $!.is_a?(SystemExit) && $!.success?
- @exit_status_socket.puts(0)
- else
- code = $!.is_a?(SystemExit) ? $!.status : 1
- @exit_status_socket.puts(code)
- end
-
- @exit_status_socket.close
- @terminal.close
- end
- end
-
- def run_action
- @action.call
- rescue StandardError => error
- ErrorPrinter.new(error).write_to($stderr)
- raise
- end
-
- def reopen_streams(i, o, e)
- $stdin.reopen(i)
- $stdout.reopen(o)
- $stderr.reopen(e)
- end
-
- def reconnect_things!
- reconnect_activerecord
- restart_girl_friday
- end
-
- def restart_girl_friday
- return unless defined?(GirlFriday::WorkQueue)
- # The Actor is run in a thread, and threads don't persist post-fork.
- # We just need to restart each one in the newly-forked process.
- ObjectSpace.each_object(GirlFriday::WorkQueue) do |obj|
- obj.send(:start)
- end
- end
-
- def reconnect_activerecord
- ActiveRecord::Base.clear_all_connections! rescue nil
- ActiveRecord::Base.establish_connection rescue nil
- end
-
- end
- end
-end
View
8 lib/zeus/server/file_monitor.rb
@@ -1,8 +0,0 @@
-require 'zeus/server/file_monitor/fsevent'
-
-module Zeus
- class Server
- module FileMonitor
- end
- end
-end
View
102 lib/zeus/server/file_monitor/fsevent.rb
@@ -1,102 +0,0 @@
-require 'open3'
-require 'pathname'
-
-module Zeus
- class Server
- module FileMonitor
- class FSEvent
- WRAPPER_PATH = File.expand_path("../../../../../ext/fsevents-wrapper/fsevents-wrapper", __FILE__)
-
- def datasource ; @io_out ; end
- def on_datasource_event ; handle_changed_files ; end
- def close_child_socket ; end
- def close_parent_socket ; [@io_in, @io_out].each(&:close) ; end
-
- def initialize(&change_callback)
- @change_callback = change_callback
- @io_in, @io_out, @wrapper_thread = open_wrapper
- @givenpath_to_realpath = {}
- @realpath_to_givenpath = {}
- @buffer = ""
- end
-
- # The biggest complicating factor here is that ruby doesn't fully resolve
- # symlinks in paths, but FSEvents does. We resolve all paths fully with
- # Pathname#realpath, and keep mappings in both directions.
- # It's conceivable that the same file would be required by two different paths,
- # so we keep an array and fire callbacks for all given paths matching a real
- # path when a change is detected.
- def watch(given)
- return false if @givenpath_to_realpath[given]
-
- real = realpath(given)
- @givenpath_to_realpath[given] = real
- @realpath_to_givenpath[real] ||= []
- @realpath_to_givenpath[real] << given
-
- @io_in.write("#{real}\n")
- true
- end
-
- def kill_wrapper
- Process.kill(9, @wrapper_thread.pid)
- rescue Errno::ESRCH # already dead. SIGINT to master causes this often.
- end
-
- private
-
- def realpath(file)
- Pathname.new(file).realpath.to_s
- rescue Errno::ENOENT
- file
- end
-
- def open_wrapper
- Open3.popen2e(WRAPPER_PATH)
- end
-
- def handle_changed_files
- 50.times { read_and_notify_files }
- rescue Stop
- end
-
- Stop = Class.new(Exception)
-
- def read_and_notify_files
- begin
- lines = @io_out.read_nonblock(1000)
- rescue Errno::EAGAIN
- raise Stop
- rescue EOFError
- Zeus.ui.error("fsevents-wrapper crashed.")
- Process.kill("INT", 0)
- end
- files = lines.split("\n")
- files[0] = "#{@buffer}#{files[0]}" unless @buffer == ""
- unless lines[-1] == "\n"
- @buffer = files.pop
- end
-
- files.each do |real|
- file_did_change(real)
- end
- end
-
- def file_did_change(real)
- realpaths_for_givenpath(real).each do |given|
- Zeus.ui.info("Dependency change at #{given}")
- @change_callback.call(given)
- end
- end
-
- def realpaths_for_givenpath(real)
- @realpath_to_givenpath[real] || []
- end
-
- end
- end
- end
-end
-
-
-
View
40 lib/zeus/server/load_tracking.rb
@@ -1,40 +0,0 @@
-module Zeus
- class Server
- class LoadTracking
- class << self
- attr_accessor :server
-
- def add_feature(file)
- return unless server
- path = if File.exist?(File.expand_path(file))
- File.expand_path(file)
- else
- find_in_load_path(file)
- end
- server.add_extra_feature path if path
- end
-
- private
-
- def find_in_load_path(file)
- $LOAD_PATH.map { |path| "#{path}/#{file}" }.detect{ |file| File.exist? file }
- end
- end
- end
- end
-end
-
-module Kernel
-
- def load(file, *a)
- Kernel.load(file, *a)
- end
-
- class << self
- alias_method :__load_without_zeus, :load
- def load(file, *a)
- Zeus::Server::LoadTracking.add_feature(file)
- __load_without_zeus(file, *a)
- end
- end
-end
View
89 lib/zeus/server/process_tree_monitor.rb
@@ -1,89 +0,0 @@
-module Zeus
- class Server
- class ProcessTreeMonitor
- STARTING_MARKER = "P"
- FEATURE_MARKER = "F"
-
- def datasource ; @sock ; end
- def on_datasource_event ; handle_messages ; end
- def close_child_socket ; @__CHILD__sock.close ; end
- def close_parent_socket ; @sock.close ; end
-
- def initialize(file_monitor, tree)
- @root = tree
- @file_monitor = file_monitor
-
- @sock, @__CHILD__sock = open_socketpair
- end
-
- def kill_nodes_with_feature(file)
- @root.kill_nodes_with_feature(file)
- end
-
- def kill_all_nodes
- @root.kill!
- end
-
- module ChildProcessApi
- def __CHILD__stage_starting_with_pid(name, pid)
- buffer_send("#{STARTING_MARKER}#{name}:#{pid}")
- end
-
- def __CHILD__stage_has_feature(name, feature)
- buffer_send("#{FEATURE_MARKER}#{name}:#{feature}")
- end
-
- private
-
- def buffer_send(msg)
- @__CHILD__sock.send(msg, 0)
- rescue Errno::ENOBUFS
- sleep 0.2
- retry
- end
-
- end ; include ChildProcessApi
-
- private
-
- def handle_messages
- 50.times { handle_message }
- rescue Stop
- end
-
- Stop = Class.new(Exception)
-
- def handle_message
- begin
- data = @sock.recv_nonblock(4096)
- rescue Errno::EAGAIN
- raise Stop
- end
- case data[0]
- when STARTING_MARKER
- handle_starting_message(data[1..-1])
- when FEATURE_MARKER
- handle_feature_message(data[1..-1])
- end
- end
-
- def open_socketpair
- Socket.pair(:UNIX, :DGRAM)
- end
-
- def handle_starting_message(data)
- data =~ /(.+):(\d+)/
- name, pid = $1.to_sym, $2.to_i
- @root.stage_has_pid(name, pid)
- end
-
- def handle_feature_message(data)
- data =~ /(.+?):(.*)/
- name, file = $1.to_sym, $2
- @root.stage_has_feature(name, file)
- @file_monitor.watch(file)
- end
-
- end
- end
-end
View
88 lib/zeus/server/stage.rb
@@ -1,88 +0,0 @@
-require 'zeus/server/stage/error_state'
-require 'zeus/server/stage/feature_notifier'
-
-module Zeus
- class Server
- # NONE of the code in the module is run in the master process,
- # so every communication to the master must be done with IPC.
- class Stage
-
- attr_accessor :name, :stages, :actions
- def initialize(server)
- @server = server
- end
-
- def descendent_acceptors
- @stages.map(&:descendent_acceptors).flatten
- end
-
- def run(close_parent_sockets = false)
- @pid = fork {
- setup_fork(close_parent_sockets)
- run_actions
- feature_notifier.notify_new_features
- start_child_stages
- handle_child_exit_loop!
- }
- end
-
- private
-
- def setup_fork(close_parent_sockets)
- $0 = "zeus #{process_type}: #{@name}"
- @server.__CHILD__close_parent_sockets if close_parent_sockets
- notify_started
- trap("INT") { exit }
- trap("TERM") { notify_terminated ; exit }
- ActiveRecord::Base.clear_all_connections! rescue nil
- end
-
- def feature_notifier
- FeatureNotifier.new(@server, @name)
- end
-
- def start_child_stages
- @pids = {}
- @stages.each do |stage|
- @pids[stage.run] = stage
- end
- end
-
- def run_actions
- begin
- @actions.each(&:call)
- rescue => e
- extend(ErrorState)
- handle_load_error(e)
- end
- end
-
- def handle_child_exit_loop!
- loop do
- begin
- pid = Process.wait
- rescue Errno::ECHILD
- sleep # if this is a terminal node, just let acceptors run...
- end
- stage = @pids[pid]
- @pids[stage.run] = stage
- end
- end
-
- def notify_started
- @server.__CHILD__stage_starting_with_pid(@name, Process.pid)
- Zeus.ui.info("starting #{process_type} `#{@name}`")
- end
-
- def notify_terminated
- Zeus.ui.info("killing #{process_type} `#{@name}`")
- end
-
-
- def process_type
- "spawner"
- end
-
- end
- end
-end
View
42 lib/zeus/server/stage/error_state.rb
@@ -1,42 +0,0 @@
-module Zeus
- class Server
- class Stage
-
- module ErrorState
- def handle_load_error(e)
- errored_file = full_path_of_file_from_error(e)
-
- # register all the decendent acceptors as stubs with errors
- register_acceptors_as_errors(e)
-
- feature_notifier.notify_feature(errored_file)
- feature_notifier.notify_new_features
-
- # we do not need to do anything. We wait, until a dependency changes.
- # At that point, we get killed and restarted.
- sleep
- end
-
- private
-
- def full_path_of_file_from_error(e)
- errored_file = e.backtrace[0].scan(/(.+?):\d+:in/)[0][0]
-
- # handle relative paths
- unless errored_file =~ /^\//
- errored_file = File.expand_path(errored_file, Dir.pwd)
- end
- end
-
- def register_acceptors_as_errors(e)
- descendent_acceptors.each do |acc|
- acc = acc.extend(Acceptor::ErrorState)
- acc.error = e
- acc.run
- end
- end
- end
-
- end
- end
-end
View
38 lib/zeus/server/stage/feature_notifier.rb
@@ -1,38 +0,0 @@
-module Zeus
- class Server
- class Stage
- class FeatureNotifier
-
- def initialize(server, stage_name)
- @server = server
- @stage_name = stage_name
- end
-
- def notify_new_features
- new_features = newly_loaded_features()
- $previously_loaded_features ||= []
- $previously_loaded_features |= new_features
- Thread.new {
- new_features.each { |f| notify_feature(f) }
- }
- end
-
- def notify_feature(feature)
- @server.__CHILD__stage_has_feature(@stage_name, feature)
- end
-
- private
-
- def newly_loaded_features
- old_features = defined?($previously_loaded_features) ? $previously_loaded_features : []
- ($LOADED_FEATURES + @server.extra_features) - old_features
- end
-
- end
- end
- end
-end
-
-
-
-
View
132 lib/zeus/templates/rails.rb
@@ -1,132 +0,0 @@
-begin
- require 'testrbl' # before bundler is setup so it does not need to be in the Gemfile
-rescue LoadError
-end
-
-ROOT_PATH = File.expand_path(Dir.pwd)
-
-Zeus::Server.define! do
- stage :boot do
-
- action do
- ENV_PATH = File.expand_path('config/environment', ROOT_PATH)
- BOOT_PATH = File.expand_path('config/boot', ROOT_PATH)
- APP_PATH = File.expand_path('config/application', ROOT_PATH)
-
- require BOOT_PATH
- require 'rails/all'
- end
-
- stage :default_bundle do
- action { Bundler.require(:default) }
-
- stage :development_environment do
- action do
- Bundler.require(:development)
- Rails.env = ENV['RAILS_ENV'] = "development"
- require APP_PATH
- Rails.application.require_environment!
- end
-
- command :generate, :g do
- begin
- require 'rails/generators'
- Rails.application.load_generators
- rescue LoadError # Rails 3.0 doesn't require this block to be run, but 3.2+ does
- end
- require 'rails/commands/generate'
- end
-
- command :runner, :r do
- require 'rails/commands/runner'
- end
-
- command :console, :c do
- require 'rails/commands/console'
- Rails::Console.start(Rails.application)
- end
-
- command :server, :s do
- require 'rails/commands/server'
- server = Rails::Server.new
- Dir.chdir(Rails.application.root)
- server.start
- end
-
- stage :prerake do
- action do
- require 'rake'
- load 'Rakefile'
- end
-
- command :rake do
- Rake.application.run
- end
-
- end
- end
-
- stage :test_environment do
- action do
- Bundler.require(:test)
-
- Rails.env = ENV['RAILS_ENV'] = 'test'
- require APP_PATH
-
- $rails_rake_task = 'yup' # lie to skip eager loading
- Rails.application.require_environment!
- $rails_rake_task = nil
- $LOAD_PATH.unshift(ROOT_PATH) unless $LOAD_PATH.include?(ROOT_PATH)
-