xing-root 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,144 @@
1
+ require 'xing/edicts'
2
+ require 'xing/tasks/tasklib'
3
+
4
+ module Xing
5
+ module Tasks
6
+ class Develop < Tasklib
7
+ DEFAULT_RELOAD_PORT = 35729
8
+ DEFAULT_RAILS_PORT = 3000
9
+ DEFAULT_MOBILE_PORT = 9000
10
+ DEFAULT_STATIC_PORT = 9292
11
+
12
+ default_namespace :develop
13
+
14
+ setting :port_offset
15
+ setting :reload_server_port
16
+ setting :rails_server_port
17
+ setting :mobile_server_port
18
+ setting :static_server_port
19
+ setting :manager
20
+ setting :config_dir, "../frontend"
21
+
22
+ def default_configuration
23
+ @port_offset ||=
24
+ begin
25
+ if !ENV['PORT_OFFSET'].nil?
26
+ ENV['PORT_OFFSET'].to_i.tap do |offset|
27
+ puts "Shifting server ports by #{offset}"
28
+ end
29
+ else
30
+ 0
31
+ end
32
+ end
33
+
34
+ @manager ||=
35
+ begin
36
+ require 'xing/managers/child'
37
+ require 'xing/managers/tmux'
38
+ if Managers::Tmux.available?
39
+ Managers::TmuxPane.new
40
+ else
41
+ ChildManager.new.tap do |mngr|
42
+ at_exit{ mngr.kill_all }
43
+ end
44
+ end
45
+ end.tap do |mgr|
46
+ puts "Using #{mgr.class.name}"
47
+ end
48
+ super
49
+ end
50
+
51
+ def resolve_configuration
52
+ super
53
+ self.reload_server_port = DEFAULT_RELOAD_PORT + port_offset if field_unset?(:reload_server_port)
54
+ self.rails_server_port = DEFAULT_RAILS_PORT + port_offset if field_unset?(:rails_server_port)
55
+ self.mobile_server_port = DEFAULT_MOBILE_PORT + port_offset if field_unset?(:mobile_server_port)
56
+ self.static_server_port = DEFAULT_STATIC_PORT + port_offset if field_unset?(:static_server_port)
57
+ end
58
+
59
+ def define
60
+ in_namespace do
61
+ desc "Launch a browser connected to a running development server"
62
+ edict_task :launch_browser, Edicts::LaunchBrowser
63
+
64
+ edict_task :grunt_watch, Edicts::StartChild do |gw|
65
+ gw.manager = manager
66
+ gw.label = "Grunt"
67
+ gw.child_task = in_namespace('service:grunt_watch').first
68
+ end
69
+
70
+ edict_task :compass_watch, Edicts::StartChild do |cw|
71
+ cw.manager = manager
72
+ cw.label = "Compass"
73
+ cw.child_task = in_namespace('service:compass_watch').first
74
+ end
75
+
76
+ edict_task :rails_server, Edicts::StartChild do |rs|
77
+ rs.manager = manager
78
+ rs.label = "Rails"
79
+ rs.child_task = in_namespace('service:rails_server').first
80
+ end
81
+
82
+ edict_task :sidekiq, Edicts::StartChild do |sk|
83
+ sk.manager = manager
84
+ sk.label = "Sidekiq"
85
+ sk.child_task = in_namespace('service:sidekiq').first
86
+ end
87
+
88
+ edict_task :static_assets, Edicts::StartChild do |sa|
89
+ sa.manager = manager
90
+ sa.label = "Static"
91
+ sa.child_task = in_namespace('service:static_assets').first
92
+ end
93
+
94
+ namespace :service do
95
+ edict_task :grunt_watch, Edicts::CleanRun do |gw|
96
+ gw.dir = "frontend"
97
+ gw.shell_cmd = %w{bundle exec node_modules/.bin/grunt watch:develop}
98
+ gw.env_hash = {"CUSTOM_CONFIG_DIR" => config_dir}
99
+ end
100
+ task :grunt_watch => 'frontend:setup'
101
+
102
+ edict_task :compass_watch, Edicts::CleanRun do |cw|
103
+ cw.dir = "frontend"
104
+ cw.shell_cmd = %w{bundle exec compass watch}
105
+ end
106
+
107
+ edict_task :rails_server, Edicts::CleanRun do |rs|
108
+ words = %w{bundle exec rails server}
109
+ words << "-p#{rails_server_port}" #ok
110
+
111
+ rs.dir = "backend"
112
+ rs.shell_cmd = words
113
+ end
114
+ task :rails_server => 'backend:setup'
115
+
116
+ edict_task :sidekiq, Edicts::CleanRun do |sk|
117
+ sk.dir = "backend"
118
+ sk.shell_cmd = %w{bundle exec sidekiq}
119
+ end
120
+
121
+ edict_task :static_assets, Edicts::CleanRun do |sa|
122
+ words = %w{bundle exec rackup}
123
+ words << "-p#{static_server_port}" #ok
124
+ words << "static-app.ru"
125
+
126
+ sa.dir = "backend"
127
+ sa.shell_cmd = words
128
+ sa.env_hash = {"LRD_BACKEND_PORT" => "#{rails_server_port}"}
129
+ end
130
+ end
131
+
132
+ task :wait do
133
+ manager.wait_all
134
+ end
135
+
136
+ task :startup => [:grunt_watch, :compass_watch, :sidekiq, :static_assets, :rails_server, :launch_browser]
137
+
138
+ task :all => [:startup, :wait]
139
+ end
140
+ end
141
+
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,44 @@
1
+ require 'xing/tasks/tasklib'
2
+ require 'xing/edicts/clean-run'
3
+ require 'xing/edicts/structure-checker'
4
+
5
+ module Xing
6
+ module Tasks
7
+ class Frontend < Tasklib
8
+ default_namespace :frontend
9
+ setting :dir, "frontend"
10
+
11
+ def define
12
+ in_namespace do
13
+ edict_task :npm_install, Edicts::CleanRun do |ni|
14
+ ni.shell_cmd = %w{npm install}
15
+ end
16
+
17
+ edict_task :bundle_install, Edicts::CleanRun do |bi|
18
+ bi.shell_cmd = %w{bundle check || bundle install}
19
+ end
20
+
21
+ task :check_dependencies => :npm_install
22
+
23
+ task :setup => [:npm_install, :bundle_install]
24
+
25
+ namespace :code_structure do
26
+ edict_task :app, Edicts::StructureChecker do |app|
27
+ app.dir = 'frontend/src/app'
28
+ end
29
+ edict_task :common, Edicts::StructureChecker do |common|
30
+ common.dir = 'frontend/src/common'
31
+ end
32
+ edict_task :framework, Edicts::StructureChecker do |fw|
33
+ fw.dir = 'frontend/src/framework'
34
+ fw.context_hash = { :escapes => %w{framework} }
35
+ end
36
+ end
37
+
38
+ desc "Apply code structure rules to frontend"
39
+ task :code_structure => %w[code_structure:app code_structure:common code_structure:framework]
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,79 @@
1
+ require 'xing/edicts'
2
+ require 'xing/tasks/tasklib'
3
+
4
+ module Xing
5
+ module Tasks
6
+ class Spec < Tasklib
7
+ default_namespace :spec
8
+ setting :config_dir, "../frontend"
9
+
10
+ def define
11
+ in_namespace do
12
+ edict_task :grunt_ci_test, Edicts::CleanRun do |gct|
13
+ gct.dir = "frontend"
14
+ gct.env_hash = {'CUSTOM_CONFIG_DIR' => config_dir}
15
+ gct.shell_cmd = %w{bundle exec node_modules/.bin/grunt ci-test}
16
+ end
17
+ task :grunt_ci_test => ['build:frontend:all' ]
18
+
19
+ namespace :links do
20
+ %w{index.html assets fonts}.each do |thing|
21
+ edict_task thing, Edict::Command do |l|
22
+ l.command = ["ln", "-sfn", "../../frontend/bin/#{thing}", "backend/public/#{thing}"]
23
+ end
24
+ end
25
+ end
26
+
27
+ edict_task :prepare_db, Edicts::CleanRun do |pd|
28
+ pd.dir = "backend"
29
+ pd.shell_cmd = %w{bundle exec rake db:test:prepare}
30
+ end
31
+
32
+ full_spec_edict = Edicts::CleanRun.new do |eddie|
33
+ copy_settings_to(eddie)
34
+ eddie.dir = "backend"
35
+ eddie.shell_cmd = %w{bundle exec rspec}
36
+ end
37
+ task :full, [:spec_files] => [:check_dependencies, 'frontend:code_structure', :grunt_ci_test, 'backend:setup', :prepare_db] do |t, args|
38
+ if args[:spec_files]
39
+ full_spec_edict.shell_cmd.push(args[:spec_files])
40
+ end
41
+ full_spec_edict.enact
42
+ end
43
+
44
+ desc "Run all feature specs, repeating with each browser width as default"
45
+ responsivity_edict = Edicts::CleanRun.new do |eddie|
46
+ copy_settings_to(eddie)
47
+ eddie.dir = "backend"
48
+ end
49
+ task :responsivity, [:spec_files] => ['backend:setup', :prepare_db] do |t, args|
50
+ %w{mobile small medium desktop}.each do |size|
51
+ responsivity_edict.shell_cmd = ["bundle", "exec", "rspec", "-o", "tmp/rspec_#{size}.txt"]
52
+ responsivity_edict.env_hash = {'BROWSER_SIZE' => size}
53
+ if args[:spec_files]
54
+ responsivity_edict.shell_cmd.push(args[:spec_files])
55
+ else
56
+ responsivity_edict.shell_cmd.push('spec/features')
57
+ end
58
+ responsivity_edict.enact rescue true
59
+ end
60
+ end
61
+
62
+ fast_edict = Edicts::CleanRun.new do |eddie|
63
+ copy_settings_to(eddie)
64
+ eddie.dir = "backend"
65
+ eddie.shell_cmd = %w{bundle exec rspec}
66
+ end
67
+ task :fast, [:spec_files] => ['backend:setup', :prepare_db] do |t, args|
68
+ if args[:spec_files]
69
+ fast_edict.shell_cmd.push(args[:spec_files])
70
+ else
71
+ fast_edict.shell_cmd.push("--tag").push("~type:feature")
72
+ end
73
+ fast_edict.enact
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,25 @@
1
+ require 'mattock'
2
+
3
+ module Xing
4
+ module Tasks
5
+ class Tasklib < Mattock::Tasklib
6
+ def edict_task(name, klass, &block)
7
+ edict = klass.new do |eddie|
8
+ copy_settings_to(eddie)
9
+ yield eddie if block_given?
10
+ end
11
+ edict_task = task name do
12
+ edict.enact
13
+ end
14
+
15
+ # For testing purposes
16
+ edict_task.instance_variable_set("@edict", edict)
17
+ def edict_task.edict
18
+ @edict
19
+ end
20
+
21
+ edict_task
22
+ end
23
+ end
24
+ end
25
+ end
data/lib/xing/tasks.rb ADDED
@@ -0,0 +1,8 @@
1
+ #require 'pp'
2
+ #pp $:
3
+
4
+ require 'xing/tasks/backend'
5
+ require 'xing/tasks/build'
6
+ require 'xing/tasks/develop'
7
+ require 'xing/tasks/frontend'
8
+ require 'xing/tasks/spec'
data/lib/xing-root.rb ADDED
@@ -0,0 +1,4 @@
1
+ module Xing
2
+ module Tasks
3
+ end
4
+ end
@@ -0,0 +1,43 @@
1
+ require 'xing/edicts/clean-rake'
2
+ #require 'bundler'
3
+
4
+ describe Xing::Edicts::CleanRake do
5
+
6
+ let :mock_shell do
7
+ instance_double(Caliph::Shell)
8
+ end
9
+
10
+ let :test_line do
11
+ "test:command"
12
+ end
13
+
14
+ let :command do
15
+ instance_double(Caliph::CommandLine)
16
+ end
17
+
18
+ let :result do
19
+ instance_double(Caliph::CommandRunResult)
20
+ end
21
+
22
+ let :test_dir do
23
+ "test-dir"
24
+ end
25
+
26
+ subject :clean_rake do
27
+ Xing::Edicts::CleanRake.new do |test|
28
+ test.dir = test_dir
29
+ test.task_name = test_line
30
+ test.caliph_shell = mock_shell
31
+ end
32
+ end
33
+
34
+ it "should run the command and check output" do
35
+ expect(Bundler).to receive(:with_clean_env).and_yield
36
+ expect(Dir).to receive(:chdir).with(test_dir).and_yield
37
+ allow(clean_rake).to receive(:cmd).with("bundle", "exec", "rake", test_line).and_return(command)
38
+ allow(mock_shell).to receive(:run).with(command).and_return(result)
39
+ expect(result).to receive(:must_succeed!)
40
+
41
+ clean_rake.enact
42
+ end
43
+ end
@@ -0,0 +1,43 @@
1
+ require 'xing/edicts/clean-run'
2
+ require 'bundler'
3
+
4
+ describe Xing::Edicts::CleanRun do
5
+
6
+ let :mock_shell do
7
+ instance_double(Caliph::Shell)
8
+ end
9
+
10
+ let :test_line do
11
+ %w[a test command]
12
+ end
13
+
14
+ let :command do
15
+ instance_double(Caliph::CommandLine)
16
+ end
17
+
18
+ let :result do
19
+ instance_double(Caliph::CommandRunResult)
20
+ end
21
+
22
+ let :test_dir do
23
+ "test-dir"
24
+ end
25
+
26
+ subject :clean_run do
27
+ Xing::Edicts::CleanRun.new do |test|
28
+ test.dir = test_dir
29
+ test.shell_cmd = test_line
30
+ test.caliph_shell = mock_shell
31
+ end
32
+ end
33
+
34
+ it "should run the command and check output" do
35
+ expect(Bundler).to receive(:with_clean_env).and_yield
36
+ expect(Dir).to receive(:chdir).with(test_dir).and_yield
37
+ allow(clean_run).to receive(:cmd).with(*test_line).and_return(command)
38
+ allow(mock_shell).to receive(:run).with(command).and_return(result)
39
+ expect(result).to receive(:must_succeed!)
40
+
41
+ clean_run.enact
42
+ end
43
+ end
@@ -0,0 +1,91 @@
1
+ require 'json'
2
+ require 'xing/edicts/launch-browser'
3
+
4
+ describe Xing::Edicts::LaunchBrowser do
5
+ let :mock_reload_connection do
6
+ instance_double(TCPSocket)
7
+ end
8
+
9
+ let :reload_server_port do
10
+ 37590
11
+ end
12
+
13
+ let :static_server_port do
14
+ 9292
15
+ end
16
+
17
+ let :reload_uri do
18
+ URI("http://localhost:#{reload_server_port}/changed")
19
+ end
20
+
21
+ let :static_url do
22
+ "http://localhost:#{static_server_port}/"
23
+ end
24
+
25
+ let :mock_shell do
26
+ instance_double(Caliph::Shell)
27
+ end
28
+
29
+ let :which_cmd do
30
+ instance_double(Caliph::CommandLine)
31
+ end
32
+
33
+ let :launch_cmd do
34
+ instance_double(Caliph::CommandLine)
35
+ end
36
+
37
+ let :success_result do
38
+ instance_double(Caliph::CommandRunResult)
39
+ end
40
+
41
+ subject :launch_browser do
42
+ Xing::Edicts::LaunchBrowser.new do |lb|
43
+ lb.caliph_shell = mock_shell
44
+ lb.reload_server_port = reload_server_port
45
+ lb.static_server_port = static_server_port
46
+ lb.max_wait = 0.1
47
+ end
48
+ end
49
+
50
+ before :each do
51
+ allow(TCPSocket).to receive(:new).with('localhost', reload_server_port).and_return(mock_reload_connection)
52
+ expect(mock_reload_connection).to receive(:close)
53
+ allow(Net::HTTP).to receive(:get).with(reload_uri).and_return(changed_json)
54
+
55
+ allow(launch_browser).to receive(:cmd).with('which', 'open').and_return(which_cmd)
56
+ allow(mock_shell).to receive(:run).with(which_cmd).and_return(success_result)
57
+ allow(success_result).to receive(:succeeds?).and_return(true)
58
+ allow(success_result).to receive(:must_succeed!).and_return(true)
59
+
60
+ allow(launch_browser).to receive(:cmd).with('open', static_url).and_return(launch_cmd)
61
+ end
62
+
63
+ describe "when no browser is running" do
64
+ let :changed_json do
65
+ {
66
+ :clients => []
67
+ }.to_json
68
+ end
69
+
70
+ it "should launch a new browser" do
71
+ expect(mock_shell).to receive(:run).with(launch_cmd).and_return(success_result)
72
+
73
+ launch_browser.check_required
74
+ launch_browser.subprocess_action #because fork
75
+ end
76
+ end
77
+
78
+ describe "when an existing browser is connected" do
79
+ let :changed_json do
80
+ {
81
+ :clients => ["someone else"]
82
+ }.to_json
83
+ end
84
+
85
+ it "should not launch a new browser" do
86
+ expect(mock_shell).not_to receive(:run).with(launch_cmd)
87
+ launch_browser.check_required
88
+ launch_browser.subprocess_action #because fork
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,30 @@
1
+ require 'xing/edicts/start-child'
2
+ require 'xing/managers/tmux'
3
+
4
+ describe Xing::Edicts::StartChild do
5
+ let :manager do
6
+ instance_double Xing::Managers::TmuxPane
7
+ end
8
+
9
+ let :task_label do
10
+ "My Cool Task"
11
+ end
12
+
13
+ let :task_name do
14
+ "my:cool:task"
15
+ end
16
+
17
+ subject :start_child do
18
+ Xing::Edicts::StartChild.new do |sc|
19
+ sc.manager = manager
20
+ sc.label = task_label
21
+ sc.child_task = task_name
22
+ end
23
+ end
24
+
25
+ it "should have the manager start a child" do
26
+ expect(manager).to receive(:start_child).with(task_label, task_name)
27
+
28
+ start_child.enact
29
+ end
30
+ end
@@ -0,0 +1,104 @@
1
+ require 'xing/managers/tmux'
2
+
3
+ describe Xing::Managers::TmuxPane do
4
+ let :lines do
5
+ 40
6
+ end
7
+
8
+ let :cols do
9
+ 160
10
+ end
11
+
12
+ before :each do
13
+ allow(tmux_pane).to receive(:puts)
14
+ end
15
+
16
+ def self.command(name)
17
+ let name do
18
+ instance_double(Caliph::CommandLine, name).tap do |cl|
19
+ allow(cl).to receive(:string_format).and_return("format of #{name}")
20
+ end
21
+ end
22
+ end
23
+
24
+ command(:list_cmd)
25
+ command(:new_session_command)
26
+
27
+ let (:mock_shell) { instance_double(Caliph::Shell) }
28
+ let (:list_result) { instance_double(Caliph::CommandRunResult) }
29
+ let (:new_session_result) { instance_double(Caliph::CommandRunResult) }
30
+ let (:which_tmux_result) { instance_double(Caliph::CommandRunResult) }
31
+
32
+ let :task_name do
33
+ "ha:ha:ha"
34
+ end
35
+
36
+ let :tmux_path do
37
+ "usr/bin/tmux"
38
+ end
39
+
40
+
41
+ subject :tmux_pane do
42
+ Xing::Managers::TmuxPane.new(mock_shell)
43
+ end
44
+
45
+ before :each do
46
+ allow(tmux_pane).to receive(:tput_lines).and_return(lines)
47
+ allow(tmux_pane).to receive(:tput_cols).and_return(cols)
48
+
49
+ allow(tmux_pane).to receive(:existing?).and_return(false, true)
50
+ allow(list_result).to receive(:stdout).and_return("nothing important")
51
+ allow(new_session_result).to receive(:stdout).and_return("nothing important")
52
+ allow(new_session_command).to receive(:string_format).and_return("")
53
+ allow(which_tmux_result).to receive(:stdout).and_return(tmux_path)
54
+ allow(which_tmux_result).to receive(:succeeds?)
55
+ allow(mock_shell).to receive(:run).with("which", "tmux").and_return(which_tmux_result)
56
+ allow(tmux_pane).to receive(:cmd).with(tmux_path, match(%r{\Alist-windows})).and_return(list_cmd)
57
+ allow(mock_shell).to receive(:run).with(list_cmd).and_return(list_result)
58
+ end
59
+
60
+ it "first child gets a new session" do
61
+ allow(tmux_pane).to receive(:cmd).with(tmux_path, match(%r{\Anew-session})).and_return(new_session_command)
62
+
63
+ expect(mock_shell).to receive(:run).with(new_session_command).and_return(new_session_result)
64
+ tmux_pane.start_child("something", task_name)
65
+ end
66
+
67
+ describe "second child" do
68
+ before :each do
69
+ # create first child
70
+ allow(tmux_pane).to receive(:cmd).with(tmux_path, match(%r{\Anew-session})).and_return(new_session_command)
71
+ allow(mock_shell).to receive(:run).with(new_session_command).and_return(new_session_result)
72
+ tmux_pane.start_child("something", task_name)
73
+ end
74
+
75
+ command(:new_window_command)
76
+ command(:join_pane_command)
77
+
78
+ it "gets a new pane" do
79
+ expect(tmux_pane).to receive(:cmd).with(tmux_path, match(%r{\Anew-window})).and_return(new_window_command)
80
+ expect(tmux_pane).to receive(:cmd).with(tmux_path, match(%r{\Ajoin-pane})).and_return(join_pane_command)
81
+ expect(mock_shell).to receive(:run).with(new_window_command).and_return(new_session_result)
82
+ expect(mock_shell).to receive(:run).with(join_pane_command).and_return(new_session_result)
83
+ tmux_pane.start_child("something", task_name)
84
+ end
85
+
86
+ describe "in small terminal" do
87
+
88
+ let :lines do
89
+ 15
90
+ end
91
+
92
+ let :cols do
93
+ 40
94
+ end
95
+
96
+ it "gets a new window" do
97
+ expect(tmux_pane).to receive(:cmd).with(tmux_path, match(%r{\Anew-window})).and_return(new_window_command)
98
+ expect(tmux_pane).to receive(:cmd).with(tmux_path, match(%r{\Aselect-layout})).and_return(new_window_command)
99
+ expect(mock_shell).to receive(:run).with(new_window_command).at_least(:twice).and_return(new_session_result)
100
+ tmux_pane.start_child("something", task_name)
101
+ end
102
+ end
103
+ end
104
+ end