tmux-connector 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +2 -0
- data/README.md +39 -26
- data/lib/tmux-connector/commands/start.rb +6 -2
- data/lib/tmux-connector/config_handler.rb +14 -9
- data/lib/tmux-connector/layout.rb +3 -1
- data/lib/tmux-connector/version.rb +1 -1
- data/spec/config_spec.rb +50 -0
- data/spec/fixtures/configs.yml +115 -0
- data/spec/spec_helper.rb +17 -0
- data/tmux-connector.gemspec +1 -0
- metadata +26 -3
data/.rspec
ADDED
data/README.md
CHANGED
@@ -154,7 +154,18 @@ Host dev.node-staging-135
|
|
154
154
|
<< ... >>
|
155
155
|
~~~
|
156
156
|
|
157
|
-
|
157
|
+
Every configuration file needs to define at least the folowing fields:
|
158
|
+
__'regex'__, __'regex-parts-to'__, __'group-by'__ and __'sort-by'__
|
159
|
+
|
160
|
+
Here's a minimal configuration file that could be used:
|
161
|
+
~~~yaml
|
162
|
+
regex: !ruby-regexp '^(\w+)\.(\w+)-([\w-]+)-(\d+)$'
|
163
|
+
regex-parts-to:
|
164
|
+
group-by: [1]
|
165
|
+
sort-by: [3]
|
166
|
+
~~~
|
167
|
+
|
168
|
+
And here's a 'real world' configuration file that shows of all the available
|
158
169
|
options and could be use with previous ssh config file:
|
159
170
|
|
160
171
|
~~~yaml
|
@@ -179,18 +190,18 @@ layout:
|
|
179
190
|
group-layouts:
|
180
191
|
misc:
|
181
192
|
tmux:
|
182
|
-
layout:
|
193
|
+
layout: tiled
|
183
194
|
max-panes: 6
|
184
195
|
node:
|
185
196
|
tmux:
|
186
|
-
layout:
|
197
|
+
layout: tiled
|
187
198
|
~~~
|
188
199
|
|
189
200
|
* * *
|
190
|
-
__'regex'__ field is the most important field. Some other field
|
191
|
-
this one. It provides a rule on how to parse host names from ssh
|
192
|
-
The regex should be a valid ruby regex. (If you're not familiar
|
193
|
-
regexes, consider visiting [rubulator] and playing around.)
|
201
|
+
(required) __'regex'__ field is the most important field. Some other field
|
202
|
+
reference this one. It provides a rule on how to parse host names from ssh
|
203
|
+
config file. The regex should be a valid ruby regex. (If you're not familiar
|
204
|
+
with ruby regexes, consider visiting [rubulator] and playing around.)
|
194
205
|
|
195
206
|
All host whose host names fail the regex will be ignored.
|
196
207
|
|
@@ -216,19 +227,19 @@ groups extracted from host names. Those groups are used to crate meaningful
|
|
216
227
|
layouts. I know, sounds more complex than it really is...
|
217
228
|
|
218
229
|
* * *
|
219
|
-
__'
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
before mentioned __'regex'__ field. As their names suggest, they decide which
|
225
|
-
servers constitute a group (and share layout and potentially commands) and how
|
226
|
-
to sort
|
227
|
-
serves in a group. Both fields can reference more than one regex group.
|
230
|
+
In (required) __'regex-parts-to'__ section, fields __'group-by'__ and
|
231
|
+
__'sort-by'__ are referencing before mentioned __'regex'__ field. As their
|
232
|
+
names suggest, they decide which servers constitute a group (and share layout
|
233
|
+
and potentially commands) and how to sort serves in a group. Both fields can
|
234
|
+
reference more than one regex group.
|
228
235
|
|
229
236
|
In the above example, for 'dev.database-staging-1' host name, a group to which
|
230
237
|
the host belongs would be 2nd group, which is: 'database'.
|
231
238
|
|
239
|
+
* * *
|
240
|
+
(optional) __'reject-regex'__ field is used to ignore some hosts while starting
|
241
|
+
a session.
|
242
|
+
|
232
243
|
* * *
|
233
244
|
(optional) field __'name'__ and it's (optional) subfields
|
234
245
|
__'regex-ignore-parts'__, __'separator'__ and __'prefix'__ decide how to name
|
@@ -252,25 +263,27 @@ Note that the servers from merge groups can later be referenced with both
|
|
252
263
|
original and merge-group name.
|
253
264
|
|
254
265
|
* * *
|
255
|
-
Finally, what's left is the
|
266
|
+
Finally, what's left is the (optional) __layout__ definition:
|
256
267
|
|
257
268
|
There are 2 main ways to specify a layout for a (merge-)group:
|
258
269
|
|
259
|
-
1. built-in tmux layouts
|
260
|
-
main-vertical or tiled
|
261
|
-
- defines a tmux layout and (optionally) maximum
|
262
|
-
|
263
|
-
2. custom tiled layout
|
264
|
-
- defines filed layout with maximal size of rows and
|
265
|
-
|
266
|
-
|
270
|
+
1. with option __tmux__, built-in tmux layouts: even-horizontal, even-vertical,
|
271
|
+
main-horizontal, main-vertical or tiled
|
272
|
+
- defines a tmux layout, option __'layout'__ and (optionally) maximum
|
273
|
+
number of panes in one window, __'max-panes'__, default 9
|
274
|
+
2. with option __custom__, custom tiled layout
|
275
|
+
- defines filed layout with maximal size of rows, __'max-vertical'__, and
|
276
|
+
columns, __'max-vertical'__. There is also an (optional) option
|
277
|
+
__'panes-flow'__ to specify if the panes flow from left to right
|
278
|
+
(horizontal - default) or from top to bottom (vertical)
|
279
|
+
|
280
|
+
If you don't specify layout, 'tmux tiled' will be used
|
267
281
|
|
268
282
|
The layouts are applied individually to any merge group and to any normal
|
269
283
|
(regex) group not belonging to some merge group. If there are more servers in
|
270
284
|
a group then layout allows on a single window, next window for that group is
|
271
285
|
added. Servers from different groups never share a window.
|
272
286
|
|
273
|
-
|
274
287
|
## Requirements
|
275
288
|
To be able to use the gem you should have ruby 1.9+ and tmux installed on a *nix
|
276
289
|
(Mac OS X, Linux, ...) machine. (Windows: here be dragons)
|
@@ -20,6 +20,8 @@ module TmuxConnector
|
|
20
20
|
( acc << Host.new(name, config) ) rescue nil
|
21
21
|
acc
|
22
22
|
end
|
23
|
+
raise "no hosts matching given configuration found, check your configuration file" if hosts.empty?
|
24
|
+
|
23
25
|
|
24
26
|
generate_groups
|
25
27
|
generate_merge_rules
|
@@ -45,8 +47,10 @@ module TmuxConnector
|
|
45
47
|
|
46
48
|
def generate_merge_rules()
|
47
49
|
@merge_rules = {}
|
48
|
-
config['merge-groups']
|
49
|
-
|
50
|
+
if config['merge-groups']
|
51
|
+
config['merge-groups'].each do |name, elements|
|
52
|
+
elements.each { |e| @merge_rules[e] = name }
|
53
|
+
end
|
50
54
|
end
|
51
55
|
@groups.keys.each { |e| @merge_rules[e] ||= e }
|
52
56
|
end
|
@@ -7,7 +7,6 @@ module TmuxConnector
|
|
7
7
|
def self.get_config(config_file)
|
8
8
|
config = read_config config_file
|
9
9
|
process_config! config
|
10
|
-
validate_config config
|
11
10
|
return config
|
12
11
|
end
|
13
12
|
|
@@ -21,7 +20,7 @@ module TmuxConnector
|
|
21
20
|
|
22
21
|
def self.process_config!(config)
|
23
22
|
config['regex'] = Regexp.new config['regex']
|
24
|
-
config['reject-regex'] = Regexp.new
|
23
|
+
config['reject-regex'] = Regexp.new(config['reject-regex']) if config['reject-regex']
|
25
24
|
if config['name']
|
26
25
|
c = config['name']
|
27
26
|
c['regex-ignore-parts'] ||= []
|
@@ -29,19 +28,25 @@ module TmuxConnector
|
|
29
28
|
c['prefix'] ||= ''
|
30
29
|
end
|
31
30
|
|
32
|
-
|
33
|
-
|
31
|
+
layout = config['layout'] ||= {}
|
32
|
+
|
33
|
+
if layout['default'].nil?
|
34
|
+
layout['default'] = {
|
35
|
+
'tmux' => { 'layout' => 'tiled' }
|
36
|
+
}
|
37
|
+
end
|
38
|
+
expand_layout layout['default']
|
39
|
+
|
40
|
+
if layout['group-layouts']
|
41
|
+
layout['group-layouts'].each { |k, v| expand_layout v }
|
42
|
+
end
|
34
43
|
end
|
35
44
|
|
36
|
-
def self.
|
45
|
+
def self.expand_layout(config)
|
37
46
|
if config['tmux']
|
38
47
|
config['tmux']['max-panes'] ||= 9
|
39
48
|
else
|
40
49
|
config['custom']['panes-flow'] ||= 'horizontal'
|
41
50
|
end
|
42
51
|
end
|
43
|
-
|
44
|
-
def self.validate_config(config)
|
45
|
-
# TODO
|
46
|
-
end
|
47
52
|
end
|
@@ -34,7 +34,9 @@ module TmuxConnector
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def process_layout_config()
|
37
|
-
{ 'default' => raw_config['default'] }
|
37
|
+
r = { 'default' => raw_config['default'] }
|
38
|
+
r.merge!(raw_config['group-layouts']) if raw_config['group-layouts']
|
39
|
+
return r
|
38
40
|
end
|
39
41
|
|
40
42
|
def add_group_to_layout(group_name, hosts, config)
|
data/spec/config_spec.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
require_relative '../lib/tmux-connector/config_handler'
|
5
|
+
|
6
|
+
|
7
|
+
shared_examples "config test" do |config_name|
|
8
|
+
let(:input) { configs[config_name]["input"] }
|
9
|
+
let(:expected) do
|
10
|
+
r = configs[config_name]["expected"]
|
11
|
+
%w[ regex reject-regex ].each { |e| r[e] = Regexp.new r[e] if r[e] }
|
12
|
+
r
|
13
|
+
end
|
14
|
+
|
15
|
+
it "process_config returns correct value" do
|
16
|
+
configuration = input
|
17
|
+
TmuxConnector.process_config! configuration
|
18
|
+
configuration.should == expected
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "Configuration file" do
|
23
|
+
let(:configs) do
|
24
|
+
YAML.load_file(File.expand_path '../fixtures/configs.yml', __FILE__)
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "minimal" do
|
28
|
+
it_should_behave_like "config test", 'minimal'
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "optional elements" do
|
32
|
+
describe "reject-regex" do
|
33
|
+
it_should_behave_like "config test", 'reject'
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "name" do
|
37
|
+
it_should_behave_like "config test", 'name'
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "merge-groups" do
|
41
|
+
it_should_behave_like "config test", 'merge'
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "layout" do
|
45
|
+
it_should_behave_like "config test", 'layout-default'
|
46
|
+
it_should_behave_like "config test", 'layout-group'
|
47
|
+
it_should_behave_like "config test", 'layout-both'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
minimal:
|
2
|
+
input: &input-min
|
3
|
+
regex: !ruby-regexp '^dev\.(\w+)([\w-]*)-(\d+)$'
|
4
|
+
regex-parts-to:
|
5
|
+
group-by: [0]
|
6
|
+
sort-by: [2]
|
7
|
+
expected: &expected-min
|
8
|
+
regex: !ruby-regexp '^dev\.(\w+)([\w-]*)-(\d+)$'
|
9
|
+
regex-parts-to:
|
10
|
+
group-by: [0]
|
11
|
+
sort-by: [2]
|
12
|
+
layout:
|
13
|
+
default:
|
14
|
+
tmux:
|
15
|
+
layout: tiled
|
16
|
+
max-panes: 9
|
17
|
+
|
18
|
+
reject:
|
19
|
+
input:
|
20
|
+
<<: *input-min
|
21
|
+
reject-regex: !ruby-regexp '(loadbalancer|ngx)-'
|
22
|
+
expected:
|
23
|
+
<<: *expected-min
|
24
|
+
reject-regex: !ruby-regexp '(loadbalancer|ngx)-'
|
25
|
+
|
26
|
+
name:
|
27
|
+
input:
|
28
|
+
<<: *input-min
|
29
|
+
name:
|
30
|
+
regex-ignore-parts: [1]
|
31
|
+
prefix: 'dev--'
|
32
|
+
expected:
|
33
|
+
<<: *expected-min
|
34
|
+
name:
|
35
|
+
regex-ignore-parts: [1]
|
36
|
+
prefix: 'dev--'
|
37
|
+
separator: '-'
|
38
|
+
|
39
|
+
merge:
|
40
|
+
input:
|
41
|
+
<<: *input-min
|
42
|
+
merge-groups:
|
43
|
+
misc: ['repo', 'mongodb', 'sshforwarder']
|
44
|
+
lbs: ['loadbalancer', 'ngx']
|
45
|
+
expected:
|
46
|
+
<<: *expected-min
|
47
|
+
merge-groups:
|
48
|
+
misc: ['repo', 'mongodb', 'sshforwarder']
|
49
|
+
lbs: ['loadbalancer', 'ngx']
|
50
|
+
|
51
|
+
layout-default:
|
52
|
+
input:
|
53
|
+
<<: *input-min
|
54
|
+
layout:
|
55
|
+
default:
|
56
|
+
tmux:
|
57
|
+
layout: 'main-vertical'
|
58
|
+
expected:
|
59
|
+
<<: *expected-min
|
60
|
+
layout:
|
61
|
+
default:
|
62
|
+
tmux:
|
63
|
+
layout: 'main-vertical'
|
64
|
+
max-panes: 9
|
65
|
+
|
66
|
+
layout-group:
|
67
|
+
input:
|
68
|
+
<<: *input-min
|
69
|
+
layout:
|
70
|
+
group-layouts:
|
71
|
+
lxc:
|
72
|
+
custom:
|
73
|
+
max-horizontal: 3
|
74
|
+
max-vertical: 3
|
75
|
+
expected:
|
76
|
+
<<: *expected-min
|
77
|
+
layout:
|
78
|
+
default:
|
79
|
+
tmux:
|
80
|
+
layout: tiled
|
81
|
+
max-panes: 9
|
82
|
+
group-layouts:
|
83
|
+
lxc:
|
84
|
+
custom:
|
85
|
+
max-horizontal: 3
|
86
|
+
max-vertical: 3
|
87
|
+
panes-flow: horizontal
|
88
|
+
|
89
|
+
layout-both:
|
90
|
+
input:
|
91
|
+
<<: *input-min
|
92
|
+
layout:
|
93
|
+
default:
|
94
|
+
tmux:
|
95
|
+
layout: 'main-vertical'
|
96
|
+
max-panes: 4
|
97
|
+
group-layouts:
|
98
|
+
bee:
|
99
|
+
custom:
|
100
|
+
max-horizontal: 2
|
101
|
+
max-vertical: 2
|
102
|
+
panes-flow: vertical
|
103
|
+
expected:
|
104
|
+
<<: *expected-min
|
105
|
+
layout:
|
106
|
+
default:
|
107
|
+
tmux:
|
108
|
+
layout: 'main-vertical'
|
109
|
+
max-panes: 4
|
110
|
+
group-layouts:
|
111
|
+
bee:
|
112
|
+
custom:
|
113
|
+
max-horizontal: 2
|
114
|
+
max-vertical: 2
|
115
|
+
panes-flow: vertical
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
9
|
+
config.run_all_when_everything_filtered = true
|
10
|
+
config.filter_run :focus
|
11
|
+
|
12
|
+
# Run specs in random order to surface order dependencies. If you find an
|
13
|
+
# order dependency and want to debug it, you can fix the order by providing
|
14
|
+
# the seed, which is printed after each run.
|
15
|
+
# --seed 1234
|
16
|
+
config.order = 'random'
|
17
|
+
end
|
data/tmux-connector.gemspec
CHANGED
@@ -13,6 +13,7 @@ Gem::Specification.new do |gem|
|
|
13
13
|
gem.homepage = "http://www.ikusalic.com"
|
14
14
|
|
15
15
|
gem.add_dependency('docopt')
|
16
|
+
gem.add_development_dependency('rspec')
|
16
17
|
|
17
18
|
gem.files = `git ls-files`.split($/)
|
18
19
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tmux-connector
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-06-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: docopt
|
@@ -27,6 +27,22 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
30
46
|
description: tcon enables establishing connections (ssh) to multiple servers and executing
|
31
47
|
commands on those servers. The sessions can be persisted (actually recreated) even
|
32
48
|
after computer restarts. Complex sessions with different layouts for different kinds
|
@@ -39,6 +55,7 @@ extensions: []
|
|
39
55
|
extra_rdoc_files: []
|
40
56
|
files:
|
41
57
|
- .gitignore
|
58
|
+
- .rspec
|
42
59
|
- Gemfile
|
43
60
|
- LICENSE.txt
|
44
61
|
- README.md
|
@@ -60,6 +77,9 @@ files:
|
|
60
77
|
- lib/tmux-connector/ssh_config_parser.rb
|
61
78
|
- lib/tmux-connector/tmux_handler.rb
|
62
79
|
- lib/tmux-connector/version.rb
|
80
|
+
- spec/config_spec.rb
|
81
|
+
- spec/fixtures/configs.yml
|
82
|
+
- spec/spec_helper.rb
|
63
83
|
- tmux-connector.gemspec
|
64
84
|
homepage: http://www.ikusalic.com
|
65
85
|
licenses: []
|
@@ -85,4 +105,7 @@ rubygems_version: 1.8.25
|
|
85
105
|
signing_key:
|
86
106
|
specification_version: 3
|
87
107
|
summary: Manage multiple servers using SSH and tmux.
|
88
|
-
test_files:
|
108
|
+
test_files:
|
109
|
+
- spec/config_spec.rb
|
110
|
+
- spec/fixtures/configs.yml
|
111
|
+
- spec/spec_helper.rb
|