xing-root 0.0.1

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.
@@ -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