sconb 1.0.0 → 1.1.0
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +74 -0
- data/.travis.yml +4 -1
- data/Rakefile +5 -6
- data/bin/console +7 -0
- data/bin/setup +7 -0
- data/{bin → exe}/sconb +0 -0
- data/lib/sconb/cli.rb +83 -0
- data/lib/sconb/ext/net/ssh/config.rb +89 -0
- data/lib/sconb/ext.rb +1 -0
- data/lib/sconb/ssh_config.rb +58 -0
- data/lib/sconb/version.rb +1 -1
- data/lib/sconb.rb +5 -205
- data/sconb.gemspec +22 -18
- metadata +60 -21
- data/spec/config_test +0 -7
- data/spec/config_test_multi +0 -18
- data/spec/github_rsa +0 -1
- data/spec/sconb_spec.rb +0 -231
- data/spec/spec_helper.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5ccdaa85d6f589d537c34eef40e28fa8c1b4f4b5
|
4
|
+
data.tar.gz: 975556b3a5d742fe0694b3ae5c0df3f00d712b4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ca88cf90196e3f11eb1df69dcabd83718063f0a1a2aa634fda6aa3b0fd152c4661fce48eed873214c62fffa9215db1c10d32573de6b6f8d02e6c58901de4550
|
7
|
+
data.tar.gz: f0a1ea0d98d59c4139d97d364f0748fd1f5241f6e67c4c3744428f9ce01432f7195fbe2b454771ba43ca42c50dbca02af7f549bbefe5b36b25869b3a65e19dce
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.1
|
3
|
+
Exclude:
|
4
|
+
- 'lib/koma/ext/**/*'
|
5
|
+
- 'spec/specinfra/**/*'
|
6
|
+
|
7
|
+
Lint/Eval:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Lint/HandleExceptions:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Lint/UselessAssignment:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
Metrics/AbcSize:
|
17
|
+
Max: 50
|
18
|
+
|
19
|
+
Metrics/ClassLength:
|
20
|
+
Max: 120
|
21
|
+
|
22
|
+
Metrics/CyclomaticComplexity:
|
23
|
+
Max: 15
|
24
|
+
|
25
|
+
Metrics/LineLength:
|
26
|
+
Max: 120
|
27
|
+
|
28
|
+
Metrics/MethodLength:
|
29
|
+
Max: 30
|
30
|
+
|
31
|
+
Metrics/PerceivedComplexity:
|
32
|
+
Max: 15
|
33
|
+
|
34
|
+
Performance/StringReplacement:
|
35
|
+
Enabled: false
|
36
|
+
|
37
|
+
Performance/Casecmp:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Style/Alias:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
Style/BarePercentLiterals:
|
44
|
+
Enabled: false
|
45
|
+
|
46
|
+
Style/ClassAndModuleChildren:
|
47
|
+
Enabled: false
|
48
|
+
|
49
|
+
Style/Documentation:
|
50
|
+
Enabled: false
|
51
|
+
|
52
|
+
Style/MutableConstant:
|
53
|
+
Enabled: false
|
54
|
+
|
55
|
+
Style/MultilineOperationIndentation:
|
56
|
+
Enabled: false
|
57
|
+
|
58
|
+
Style/StabbyLambdaParentheses:
|
59
|
+
Enabled: false
|
60
|
+
|
61
|
+
Style/PercentLiteralDelimiters:
|
62
|
+
Enabled: false
|
63
|
+
|
64
|
+
Style/PredicateName:
|
65
|
+
Enabled: false
|
66
|
+
|
67
|
+
Style/RedundantSelf:
|
68
|
+
Enabled: false
|
69
|
+
|
70
|
+
Style/SymbolProc:
|
71
|
+
Enabled: false
|
72
|
+
|
73
|
+
Style/BracesAroundHashParameters:
|
74
|
+
Enabled: false
|
data/.travis.yml
CHANGED
data/Rakefile
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'octorelease'
|
2
4
|
|
3
|
-
|
4
|
-
require 'rspec/core/rake_task'
|
5
|
-
RSpec::Core::RakeTask.new(:spec)
|
6
|
-
rescue LoadError
|
7
|
-
end
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
8
6
|
|
7
|
+
task default: :spec
|
data/bin/console
ADDED
data/bin/setup
ADDED
data/{bin → exe}/sconb
RENAMED
File without changes
|
data/lib/sconb/cli.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Sconb
|
4
|
+
class CLI < Thor
|
5
|
+
option :all,
|
6
|
+
type: :boolean,
|
7
|
+
aliases: :a,
|
8
|
+
default: false,
|
9
|
+
banner: 'dump .ssh/config and private keys.'
|
10
|
+
option :config,
|
11
|
+
type: :string,
|
12
|
+
aliases: :c,
|
13
|
+
default: '~/.ssh/config',
|
14
|
+
banner: '.ssh/config path'
|
15
|
+
desc 'dump > dump.json', 'Dump .ssh/config to JSON'
|
16
|
+
def dump(regexp_str = '.*')
|
17
|
+
path = options[:config]
|
18
|
+
puts JSON.pretty_generate(Sconb::SSHConfig.load(path, regexp_str, options))
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'restore < dump.json > .ssh/config', 'Restore .ssh/config from JSON'
|
22
|
+
def restore
|
23
|
+
ssh_configs = []
|
24
|
+
json = stdin_read
|
25
|
+
configs = JSON.parse(json)
|
26
|
+
configs.each do |host, config|
|
27
|
+
ssh_config = ''
|
28
|
+
header = if host !~ /^Match /
|
29
|
+
"Host #{host}\n"
|
30
|
+
else
|
31
|
+
"#{host}\n"
|
32
|
+
end
|
33
|
+
ssh_config << header
|
34
|
+
config.each do |key, value|
|
35
|
+
next if key.downcase == 'host' || key.downcase == 'match' || key.downcase == 'identityfilecontent'
|
36
|
+
if key.downcase == 'identityfile'
|
37
|
+
value.each_with_index do |keyfile, _i|
|
38
|
+
ssh_config << " #{key} #{keyfile}\n"
|
39
|
+
end
|
40
|
+
else
|
41
|
+
ssh_config << " #{key} #{value}\n"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
ssh_configs.push ssh_config
|
45
|
+
end
|
46
|
+
puts ssh_configs.join("\n")
|
47
|
+
end
|
48
|
+
|
49
|
+
option :force,
|
50
|
+
type: :boolean,
|
51
|
+
aliases: :f,
|
52
|
+
default: false,
|
53
|
+
banner: 'force generate'
|
54
|
+
desc 'keyregen < dump.json', 'Regenerate private keys from JSON'
|
55
|
+
def keyregen
|
56
|
+
json = stdin_read
|
57
|
+
configs = JSON.parse(json)
|
58
|
+
configs.each do |_host, config|
|
59
|
+
config.each do |key, value|
|
60
|
+
next unless key.downcase == 'identityfilecontent'
|
61
|
+
identity_files = config['IdentityFile']
|
62
|
+
value.each_with_index do |keycontent, i|
|
63
|
+
identity_file = File.expand_path(identity_files[i])
|
64
|
+
if File.exist?(identity_file) && !options[:force]
|
65
|
+
raise Thor::Error, "Error: #{identity_files[i]} is exists. If you want to overwrite, use --force option."
|
66
|
+
end
|
67
|
+
puts "Regenerate #{identity_files[i]} ..."
|
68
|
+
File.open(identity_file, 'w') do |file|
|
69
|
+
file.write keycontent
|
70
|
+
end
|
71
|
+
File.chmod(0600, identity_file)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def stdin_read
|
80
|
+
$stdin.read
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Net
|
2
|
+
module SSH
|
3
|
+
class Config
|
4
|
+
class << self
|
5
|
+
# Original code is Net::SSH::Config.load (https://github.com/net-ssh/net-ssh/blob/master/LICENSE.txt)
|
6
|
+
# rubocop:disable all
|
7
|
+
def sconb_load(path, host, options)
|
8
|
+
settings = {}
|
9
|
+
file = File.expand_path(path)
|
10
|
+
return settings unless File.readable?(file)
|
11
|
+
|
12
|
+
globals = {}
|
13
|
+
matched_host = nil
|
14
|
+
multi_host = []
|
15
|
+
seen_host = false
|
16
|
+
IO.foreach(file) do |line|
|
17
|
+
next if line =~ /^\s*(?:#.*)?$/
|
18
|
+
|
19
|
+
if line =~ /^\s*(\S+)\s*=(.*)$/
|
20
|
+
key, value = $1, $2
|
21
|
+
else
|
22
|
+
key, value = line.strip.split(/\s+/, 2)
|
23
|
+
end
|
24
|
+
|
25
|
+
# silently ignore malformed entries
|
26
|
+
next if value.nil?
|
27
|
+
|
28
|
+
value = $1 if value =~ /^"(.*)"$/
|
29
|
+
|
30
|
+
if key.downcase == 'host'
|
31
|
+
# Support "Host host1 host2 hostN".
|
32
|
+
# See http://github.com/net-ssh/net-ssh/issues#issue/6
|
33
|
+
negative_hosts, positive_hosts = value.to_s.split(/\s+/).partition { |h| h.start_with?('!') }
|
34
|
+
|
35
|
+
# Check for negative patterns first. If the host matches, that overrules any other positive match.
|
36
|
+
# The host substring code is used to strip out the starting "!" so the regexp will be correct.
|
37
|
+
negative_match = negative_hosts.select { |h| host =~ pattern2regex(h[1..-1]) }.first
|
38
|
+
|
39
|
+
if negative_match
|
40
|
+
matched_host = nil
|
41
|
+
else
|
42
|
+
matched_host = positive_hosts.select { |h| host =~ pattern2regex(h) }.first
|
43
|
+
end
|
44
|
+
settings[key] = host unless matched_host.nil?
|
45
|
+
seen_host = true
|
46
|
+
elsif key.downcase == 'match'
|
47
|
+
if host == value
|
48
|
+
matched_host = true
|
49
|
+
else
|
50
|
+
matched_host = nil
|
51
|
+
end
|
52
|
+
settings[key] = host unless matched_host.nil?
|
53
|
+
seen_host = true
|
54
|
+
elsif !seen_host
|
55
|
+
if key.downcase == 'identityfile'
|
56
|
+
(globals[key] ||= []) << value
|
57
|
+
|
58
|
+
# Read IdentityFile Content
|
59
|
+
identity_file = File.expand_path(value)
|
60
|
+
if options[:all] and File.readable?(identity_file)
|
61
|
+
(globals['IdentityFileContent'] ||= []) << File.open(identity_file).read
|
62
|
+
end
|
63
|
+
else
|
64
|
+
globals[key] = value unless settings.key?(key)
|
65
|
+
end
|
66
|
+
elsif !matched_host.nil?
|
67
|
+
if key.downcase == 'identityfile'
|
68
|
+
(settings[key] ||= []) << value
|
69
|
+
|
70
|
+
# Read IdentityFile Content
|
71
|
+
identity_file = File.expand_path(value)
|
72
|
+
if options[:all] and File.readable?(identity_file)
|
73
|
+
(settings['IdentityFileContent'] ||= []) << File.open(identity_file).read
|
74
|
+
end
|
75
|
+
else
|
76
|
+
settings[key] = value unless settings.key?(key)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
settings = globals.merge(settings) if globals
|
82
|
+
|
83
|
+
settings
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
data/lib/sconb/ext.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'sconb/ext/net/ssh/config'
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Sconb
|
2
|
+
module SSHConfig
|
3
|
+
class << self
|
4
|
+
def load(path, regexp_str = '.*', options = [])
|
5
|
+
@path = path
|
6
|
+
@regexp = Regexp.new(regexp_str)
|
7
|
+
@options = options
|
8
|
+
file = File.expand_path(@path)
|
9
|
+
@configs = {}
|
10
|
+
return @configs unless File.readable?(file)
|
11
|
+
|
12
|
+
@allconfig = Net::SSH::Config.sconb_load(@path, '*', @options)
|
13
|
+
@configs['*'] = @allconfig unless @allconfig.size <= 1
|
14
|
+
IO.foreach(file) do |line|
|
15
|
+
parse(line)
|
16
|
+
end
|
17
|
+
@configs
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def parse(line)
|
23
|
+
return if line =~ /^\s*(?:#.*)?$/
|
24
|
+
if line =~ /^\s*(\S+)\s*=(.*)$/
|
25
|
+
key = Regexp.last_match[1]
|
26
|
+
value = Regexp.last_match[2]
|
27
|
+
else
|
28
|
+
key, value = line.strip.split(/\s+/, 2)
|
29
|
+
end
|
30
|
+
return if value.nil?
|
31
|
+
|
32
|
+
# Host
|
33
|
+
if key.downcase == 'host'
|
34
|
+
negative_hosts, positive_hosts = value.to_s.split(/\s+/).partition { |h| h.start_with?('!') }
|
35
|
+
positive_hosts.each do |host|
|
36
|
+
next if host == '*'
|
37
|
+
next unless host.match @regexp
|
38
|
+
config = Net::SSH::Config.sconb_load(@path, host, @options)
|
39
|
+
|
40
|
+
@allconfig.each do |k, _v|
|
41
|
+
next unless config.key? k
|
42
|
+
config.delete k if config[k] == @allconfig[k]
|
43
|
+
end
|
44
|
+
|
45
|
+
@configs[host] = config
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Match
|
50
|
+
if key.downcase == 'match'
|
51
|
+
match_key = key + ' ' + value
|
52
|
+
return unless match_key.match @regexp
|
53
|
+
@configs[match_key] = Net::SSH::Config.sconb_load(@path, value, @options)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/sconb/version.rb
CHANGED
data/lib/sconb.rb
CHANGED
@@ -1,208 +1,8 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
1
|
+
require 'net/ssh'
|
2
|
+
require 'sconb/version'
|
3
|
+
require 'sconb/ext'
|
4
|
+
require 'sconb/ssh_config'
|
5
|
+
require 'sconb/cli'
|
5
6
|
|
6
7
|
module Sconb
|
7
|
-
class CLI < Thor
|
8
|
-
|
9
|
-
method_option :all, :type => :boolean, :aliases => '-a', :default => false, :banner => 'dump .ssh/config and private keys.'
|
10
|
-
method_option :config, :type => :string, :aliases => '-c', :default => '~/.ssh/config', :banner => '.ssh/config path'
|
11
|
-
desc "dump > dump.json", "Dump .ssh/config to JSON"
|
12
|
-
def dump(regexp_str = '.*')
|
13
|
-
regexp = Regexp.new regexp_str
|
14
|
-
path = options[:config]
|
15
|
-
file = File.expand_path(path)
|
16
|
-
configs = {}
|
17
|
-
unless File.readable?(file)
|
18
|
-
puts configs
|
19
|
-
return
|
20
|
-
end
|
21
|
-
|
22
|
-
allconfig = config_load(path, '*')
|
23
|
-
configs['*'] = allconfig unless allconfig.size <= 1
|
24
|
-
IO.foreach(file) do |line|
|
25
|
-
next if line =~ /^\s*(?:#.*)?$/
|
26
|
-
if line =~ /^\s*(\S+)\s*=(.*)$/
|
27
|
-
key, value = $1, $2
|
28
|
-
else
|
29
|
-
key, value = line.strip.split(/\s+/, 2)
|
30
|
-
end
|
31
|
-
next if value.nil?
|
32
|
-
|
33
|
-
# Host
|
34
|
-
if key.downcase == 'host'
|
35
|
-
negative_hosts, positive_hosts = value.to_s.split(/\s+/).partition { |h| h.start_with?('!') }
|
36
|
-
positive_hosts.each do | host |
|
37
|
-
next if host == '*'
|
38
|
-
next unless host.match regexp
|
39
|
-
config = config_load(path, host)
|
40
|
-
|
41
|
-
allconfig.each do |key, value|
|
42
|
-
next unless config.key? key
|
43
|
-
config.delete key if config[key] == allconfig[key]
|
44
|
-
end
|
45
|
-
|
46
|
-
configs[host] = config
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
# Match
|
51
|
-
if key.downcase == 'match'
|
52
|
-
match_key = key + ' ' + value
|
53
|
-
next unless match_key.match regexp
|
54
|
-
configs[match_key] = config_load(path, value)
|
55
|
-
end
|
56
|
-
|
57
|
-
end
|
58
|
-
puts JSON.pretty_generate configs
|
59
|
-
end
|
60
|
-
|
61
|
-
desc "restore < dump.json > .ssh/config", "Restore .ssh/config from JSON"
|
62
|
-
def restore()
|
63
|
-
ssh_configs = []
|
64
|
-
json = stdin_read
|
65
|
-
configs = JSON.parse(json)
|
66
|
-
configs.each do |host, config|
|
67
|
-
ssh_config = ''
|
68
|
-
unless host.match(/^Match /)
|
69
|
-
ssh_config << 'Host ' + host + "\n"
|
70
|
-
else
|
71
|
-
ssh_config << host + "\n"
|
72
|
-
end
|
73
|
-
config.each do |key, value|
|
74
|
-
next if key.downcase == 'host' || key.downcase == 'match' || key.downcase == 'identityfilecontent'
|
75
|
-
if key.downcase == 'identityfile'
|
76
|
-
value.each_with_index do |keyfile,i|
|
77
|
-
ssh_config << ' ' + key + ' ' + keyfile + "\n"
|
78
|
-
end
|
79
|
-
else
|
80
|
-
ssh_config << ' ' + key + ' ' + value + "\n"
|
81
|
-
end
|
82
|
-
end
|
83
|
-
ssh_configs.push ssh_config
|
84
|
-
end
|
85
|
-
puts ssh_configs.join("\n")
|
86
|
-
end
|
87
|
-
|
88
|
-
method_option :force, :type => :boolean, :aliases => '-f', :default => false, :banner => 'force generate'
|
89
|
-
desc "keyregen < dump.json", "Regenerate private keys from JSON"
|
90
|
-
def keyregen()
|
91
|
-
json = stdin_read
|
92
|
-
configs = JSON.parse(json)
|
93
|
-
configs.each do |host, config|
|
94
|
-
config.each do |key, value|
|
95
|
-
next unless key.downcase == 'identityfilecontent'
|
96
|
-
identity_files = config['IdentityFile']
|
97
|
-
value.each_with_index do |keycontent,i|
|
98
|
-
identity_file = File.expand_path(identity_files[i])
|
99
|
-
if File.exists?(identity_file) and !options[:force]
|
100
|
-
raise Thor::Error, "Error: " + identity_files[i] + " is exists. If you want to overwrite, use --force option."
|
101
|
-
end
|
102
|
-
puts 'Regenerate ' + identity_files[i] + ' ...'
|
103
|
-
File.open(identity_file, 'w') do |file|
|
104
|
-
file.write keycontent
|
105
|
-
end
|
106
|
-
File.chmod(0600, identity_file)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
private
|
113
|
-
def stdin_read()
|
114
|
-
return $stdin.read
|
115
|
-
end
|
116
|
-
|
117
|
-
# Original code is Net::SSH::Config.load (https://github.com/net-ssh/net-ssh/blob/master/LICENSE.txt)
|
118
|
-
private
|
119
|
-
def config_load(path, host)
|
120
|
-
settings = {}
|
121
|
-
file = File.expand_path(path)
|
122
|
-
return settings unless File.readable?(file)
|
123
|
-
|
124
|
-
globals = {}
|
125
|
-
matched_host = nil
|
126
|
-
multi_host = []
|
127
|
-
seen_host = false
|
128
|
-
IO.foreach(file) do |line|
|
129
|
-
next if line =~ /^\s*(?:#.*)?$/
|
130
|
-
|
131
|
-
if line =~ /^\s*(\S+)\s*=(.*)$/
|
132
|
-
key, value = $1, $2
|
133
|
-
else
|
134
|
-
key, value = line.strip.split(/\s+/, 2)
|
135
|
-
end
|
136
|
-
|
137
|
-
# silently ignore malformed entries
|
138
|
-
next if value.nil?
|
139
|
-
|
140
|
-
value = $1 if value =~ /^"(.*)"$/
|
141
|
-
|
142
|
-
if key.downcase == 'host'
|
143
|
-
# Support "Host host1 host2 hostN".
|
144
|
-
# See http://github.com/net-ssh/net-ssh/issues#issue/6
|
145
|
-
negative_hosts, positive_hosts = value.to_s.split(/\s+/).partition { |h| h.start_with?('!') }
|
146
|
-
|
147
|
-
# Check for negative patterns first. If the host matches, that overrules any other positive match.
|
148
|
-
# The host substring code is used to strip out the starting "!" so the regexp will be correct.
|
149
|
-
negative_match = negative_hosts.select { |h| host =~ pattern2regex(h[1..-1]) }.first
|
150
|
-
|
151
|
-
if negative_match
|
152
|
-
matched_host = nil
|
153
|
-
else
|
154
|
-
matched_host = positive_hosts.select { |h| host =~ pattern2regex(h) }.first
|
155
|
-
end
|
156
|
-
settings[key] = host unless matched_host.nil?
|
157
|
-
seen_host = true
|
158
|
-
elsif key.downcase == 'match'
|
159
|
-
if host == value
|
160
|
-
matched_host = true
|
161
|
-
else
|
162
|
-
matched_host = nil
|
163
|
-
end
|
164
|
-
settings[key] = host unless matched_host.nil?
|
165
|
-
seen_host = true
|
166
|
-
elsif !seen_host
|
167
|
-
if key.downcase == 'identityfile'
|
168
|
-
(globals[key] ||= []) << value
|
169
|
-
|
170
|
-
# Read IdentityFile Content
|
171
|
-
identity_file = File.expand_path(value)
|
172
|
-
if options[:all] and File.readable?(identity_file)
|
173
|
-
(globals['IdentityFileContent'] ||= []) << File.open(identity_file).read
|
174
|
-
end
|
175
|
-
else
|
176
|
-
globals[key] = value unless settings.key?(key)
|
177
|
-
end
|
178
|
-
elsif !matched_host.nil?
|
179
|
-
if key.downcase == 'identityfile'
|
180
|
-
(settings[key] ||= []) << value
|
181
|
-
|
182
|
-
# Read IdentityFile Content
|
183
|
-
identity_file = File.expand_path(value)
|
184
|
-
if options[:all] and File.readable?(identity_file)
|
185
|
-
(settings['IdentityFileContent'] ||= []) << File.open(identity_file).read
|
186
|
-
end
|
187
|
-
else
|
188
|
-
settings[key] = value unless settings.key?(key)
|
189
|
-
end
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
settings = globals.merge(settings) if globals
|
194
|
-
|
195
|
-
return settings
|
196
|
-
end
|
197
|
-
|
198
|
-
private
|
199
|
-
# Original code is Net::SSH::Config.pattern2regex (https://github.com/net-ssh/net-ssh/blob/master/LICENSE.txt)
|
200
|
-
def pattern2regex(pattern)
|
201
|
-
pattern = "^" + pattern.to_s.gsub(/\./, "\\.").
|
202
|
-
gsub(/\?/, '.').
|
203
|
-
gsub(/([+\/])/, '\\\\\\0').
|
204
|
-
gsub(/\*/, '.*') + "$"
|
205
|
-
Regexp.new(pattern, true)
|
206
|
-
end
|
207
|
-
end
|
208
8
|
end
|
data/sconb.gemspec
CHANGED
@@ -4,25 +4,29 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'sconb/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'sconb'
|
8
8
|
spec.version = Sconb::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
9
|
+
spec.authors = ['k1LoW']
|
10
|
+
spec.email = ['k1lowxb@gmail.com']
|
11
|
+
spec.summary = 'Ssh CONfig Buckup tool.'
|
12
|
+
spec.description = 'Ssh CONfig Buckup tool.'
|
13
|
+
spec.homepage = 'https://github.com/k1LoW/sconb'
|
14
|
+
spec.license = 'MIT'
|
15
15
|
|
16
|
-
spec.files = `git ls-files -z`.split("\x0")
|
17
|
-
spec.
|
18
|
-
spec.
|
19
|
-
spec.require_paths = [
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = 'exe'
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ['lib']
|
20
20
|
|
21
|
-
spec.
|
22
|
-
spec.add_runtime_dependency
|
23
|
-
spec.
|
24
|
-
spec.
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.
|
21
|
+
spec.required_ruby_version = '>= 2.1'
|
22
|
+
spec.add_runtime_dependency 'thor'
|
23
|
+
spec.add_runtime_dependency 'net-ssh'
|
24
|
+
spec.add_runtime_dependency 'json'
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.9'
|
26
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
27
|
+
spec.add_development_dependency 'rspec'
|
28
|
+
spec.add_development_dependency 'coveralls'
|
29
|
+
spec.add_development_dependency 'octorelease'
|
30
|
+
spec.add_development_dependency 'rubocop'
|
31
|
+
spec.add_development_dependency 'pry'
|
28
32
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sconb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- k1LoW
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: net-ssh
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,14 +58,14 @@ dependencies:
|
|
44
58
|
requirements:
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '1.
|
61
|
+
version: '1.9'
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: '1.
|
68
|
+
version: '1.9'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: rake
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -95,13 +109,41 @@ dependencies:
|
|
95
109
|
- !ruby/object:Gem::Version
|
96
110
|
version: '0'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
112
|
+
name: octorelease
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
100
114
|
requirements:
|
101
115
|
- - ">="
|
102
116
|
- !ruby/object:Gem::Version
|
103
117
|
version: '0'
|
104
|
-
type: :
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rubocop
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: pry
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
105
147
|
prerelease: false
|
106
148
|
version_requirements: !ruby/object:Gem::Requirement
|
107
149
|
requirements:
|
@@ -119,22 +161,24 @@ files:
|
|
119
161
|
- ".coveralls.yml"
|
120
162
|
- ".gitignore"
|
121
163
|
- ".rspec"
|
164
|
+
- ".rubocop.yml"
|
122
165
|
- ".tachikoma.yml"
|
123
166
|
- ".travis.yml"
|
124
167
|
- Gemfile
|
125
168
|
- LICENSE.txt
|
126
169
|
- README.md
|
127
170
|
- Rakefile
|
128
|
-
- bin/
|
171
|
+
- bin/console
|
172
|
+
- bin/setup
|
173
|
+
- exe/sconb
|
129
174
|
- lib/sconb.rb
|
175
|
+
- lib/sconb/cli.rb
|
176
|
+
- lib/sconb/ext.rb
|
177
|
+
- lib/sconb/ext/net/ssh/config.rb
|
178
|
+
- lib/sconb/ssh_config.rb
|
130
179
|
- lib/sconb/version.rb
|
131
180
|
- sconb.gemspec
|
132
|
-
|
133
|
-
- spec/config_test_multi
|
134
|
-
- spec/github_rsa
|
135
|
-
- spec/sconb_spec.rb
|
136
|
-
- spec/spec_helper.rb
|
137
|
-
homepage: ''
|
181
|
+
homepage: https://github.com/k1LoW/sconb
|
138
182
|
licenses:
|
139
183
|
- MIT
|
140
184
|
metadata: {}
|
@@ -146,7 +190,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
146
190
|
requirements:
|
147
191
|
- - ">="
|
148
192
|
- !ruby/object:Gem::Version
|
149
|
-
version: '
|
193
|
+
version: '2.1'
|
150
194
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
195
|
requirements:
|
152
196
|
- - ">="
|
@@ -158,9 +202,4 @@ rubygems_version: 2.2.2
|
|
158
202
|
signing_key:
|
159
203
|
specification_version: 4
|
160
204
|
summary: Ssh CONfig Buckup tool.
|
161
|
-
test_files:
|
162
|
-
- spec/config_test
|
163
|
-
- spec/config_test_multi
|
164
|
-
- spec/github_rsa
|
165
|
-
- spec/sconb_spec.rb
|
166
|
-
- spec/spec_helper.rb
|
205
|
+
test_files: []
|
data/spec/config_test
DELETED
data/spec/config_test_multi
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
Host github.com
|
2
|
-
User git
|
3
|
-
Port 22
|
4
|
-
Hostname github.com
|
5
|
-
IdentityFile spec/github_rsa
|
6
|
-
TCPKeepAlive yes
|
7
|
-
IdentitiesOnly yes
|
8
|
-
|
9
|
-
Match exec "nmcli connection status id <ap-name> 2> /dev/null"
|
10
|
-
ProxyCommand ssh -W %h:%p github.com
|
11
|
-
|
12
|
-
Host gist
|
13
|
-
User git
|
14
|
-
Port 22
|
15
|
-
Hostname gist.github.com
|
16
|
-
IdentityFile spec/github_rsa
|
17
|
-
TCPKeepAlive yes
|
18
|
-
IdentitiesOnly yes
|
data/spec/github_rsa
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
1234567890
|
data/spec/sconb_spec.rb
DELETED
@@ -1,231 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe Sconb do
|
5
|
-
context '`dump` command' do
|
6
|
-
|
7
|
-
it "should convert from .ssh/config to JSON" do
|
8
|
-
expect(capture(:stdout) {
|
9
|
-
Sconb::CLI.new.invoke(:dump, [], {config: File.expand_path('../config_test', __FILE__)})
|
10
|
-
}).to eq <<OUT
|
11
|
-
{
|
12
|
-
"github.com": {
|
13
|
-
"Host": "github.com",
|
14
|
-
"User": "git",
|
15
|
-
"Port": "22",
|
16
|
-
"Hostname": "github.com",
|
17
|
-
"IdentityFile": [
|
18
|
-
"spec/github_rsa"
|
19
|
-
],
|
20
|
-
"TCPKeepAlive": "yes",
|
21
|
-
"IdentitiesOnly": "yes"
|
22
|
-
}
|
23
|
-
}
|
24
|
-
OUT
|
25
|
-
end
|
26
|
-
|
27
|
-
it "should convert from multi config to JSON" do
|
28
|
-
expect(capture(:stdout) {
|
29
|
-
Sconb::CLI.new.invoke(:dump, [], {config: File.expand_path('../config_test_multi', __FILE__)})
|
30
|
-
}).to eq <<OUT
|
31
|
-
{
|
32
|
-
"github.com": {
|
33
|
-
"Host": "github.com",
|
34
|
-
"User": "git",
|
35
|
-
"Port": "22",
|
36
|
-
"Hostname": "github.com",
|
37
|
-
"IdentityFile": [
|
38
|
-
"spec/github_rsa"
|
39
|
-
],
|
40
|
-
"TCPKeepAlive": "yes",
|
41
|
-
"IdentitiesOnly": "yes"
|
42
|
-
},
|
43
|
-
"Match exec \\"nmcli connection status id <ap-name> 2> /dev/null\\"": {
|
44
|
-
"Match": "exec \\"nmcli connection status id <ap-name> 2> /dev/null\\"",
|
45
|
-
"ProxyCommand": "ssh -W %h:%p github.com"
|
46
|
-
},
|
47
|
-
"gist": {
|
48
|
-
"Host": "gist",
|
49
|
-
"User": "git",
|
50
|
-
"Port": "22",
|
51
|
-
"Hostname": "gist.github.com",
|
52
|
-
"IdentityFile": [
|
53
|
-
"spec/github_rsa"
|
54
|
-
],
|
55
|
-
"TCPKeepAlive": "yes",
|
56
|
-
"IdentitiesOnly": "yes"
|
57
|
-
}
|
58
|
-
}
|
59
|
-
OUT
|
60
|
-
end
|
61
|
-
|
62
|
-
it "should convert from multi config to JSON with filter" do
|
63
|
-
expect(capture(:stdout) {
|
64
|
-
Sconb::CLI.new.invoke(:dump, ['gis?t'], {config: File.expand_path('../config_test_multi', __FILE__)})
|
65
|
-
}).to eq <<OUT
|
66
|
-
{
|
67
|
-
"github.com": {
|
68
|
-
"Host": "github.com",
|
69
|
-
"User": "git",
|
70
|
-
"Port": "22",
|
71
|
-
"Hostname": "github.com",
|
72
|
-
"IdentityFile": [
|
73
|
-
"spec/github_rsa"
|
74
|
-
],
|
75
|
-
"TCPKeepAlive": "yes",
|
76
|
-
"IdentitiesOnly": "yes"
|
77
|
-
},
|
78
|
-
"gist": {
|
79
|
-
"Host": "gist",
|
80
|
-
"User": "git",
|
81
|
-
"Port": "22",
|
82
|
-
"Hostname": "gist.github.com",
|
83
|
-
"IdentityFile": [
|
84
|
-
"spec/github_rsa"
|
85
|
-
],
|
86
|
-
"TCPKeepAlive": "yes",
|
87
|
-
"IdentitiesOnly": "yes"
|
88
|
-
}
|
89
|
-
}
|
90
|
-
OUT
|
91
|
-
end
|
92
|
-
|
93
|
-
it "should convert from .ssh/config to JSON with private keys" do
|
94
|
-
expect(capture(:stdout) {
|
95
|
-
Sconb::CLI.new.invoke(:dump, [], {config: File.expand_path('../config_test', __FILE__), all: true})
|
96
|
-
}).to eq <<OUT
|
97
|
-
{
|
98
|
-
"github.com": {
|
99
|
-
"Host": "github.com",
|
100
|
-
"User": "git",
|
101
|
-
"Port": "22",
|
102
|
-
"Hostname": "github.com",
|
103
|
-
"IdentityFile": [
|
104
|
-
"spec/github_rsa"
|
105
|
-
],
|
106
|
-
"IdentityFileContent": [
|
107
|
-
"1234567890"
|
108
|
-
],
|
109
|
-
"TCPKeepAlive": "yes",
|
110
|
-
"IdentitiesOnly": "yes"
|
111
|
-
}
|
112
|
-
}
|
113
|
-
OUT
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
context '`restore` command' do
|
118
|
-
before do
|
119
|
-
@cli = Sconb::CLI.new
|
120
|
-
allow(@cli).to receive_messages(:stdin_read => <<INN
|
121
|
-
{
|
122
|
-
"github.com": {
|
123
|
-
"Host": "github.com",
|
124
|
-
"User": "git",
|
125
|
-
"Port": "22",
|
126
|
-
"Hostname": "github.com",
|
127
|
-
"IdentityFile": [
|
128
|
-
"spec/github_rsa"
|
129
|
-
],
|
130
|
-
"IdentityFileContent": [
|
131
|
-
"1234567890"
|
132
|
-
],
|
133
|
-
"TCPKeepAlive": "yes",
|
134
|
-
"IdentitiesOnly": "yes"
|
135
|
-
},
|
136
|
-
"gist": {
|
137
|
-
"Host": "gist",
|
138
|
-
"User": "git",
|
139
|
-
"Port": "22",
|
140
|
-
"Hostname": "gist.github.com",
|
141
|
-
"IdentityFile": [
|
142
|
-
"spec/github_rsa"
|
143
|
-
],
|
144
|
-
"IdentityFileContent": [
|
145
|
-
"1234567890"
|
146
|
-
],
|
147
|
-
"TCPKeepAlive": "yes",
|
148
|
-
"IdentitiesOnly": "yes"
|
149
|
-
},
|
150
|
-
"Match exec \\"nmcli connection status id <ap-name> 2> /dev/null\\"": {
|
151
|
-
"Match": "exec \\"nmcli connection status id <ap-name> 2> /dev/null\\"",
|
152
|
-
"ProxyCommand": "ssh -W %h:%p github.com"
|
153
|
-
}
|
154
|
-
}
|
155
|
-
INN
|
156
|
-
)
|
157
|
-
end
|
158
|
-
|
159
|
-
it "should convert from JSON to config" do
|
160
|
-
expect(capture(:stdout) {
|
161
|
-
@cli.restore
|
162
|
-
}).to eq <<OUT
|
163
|
-
Host github.com
|
164
|
-
User git
|
165
|
-
Port 22
|
166
|
-
Hostname github.com
|
167
|
-
IdentityFile spec/github_rsa
|
168
|
-
TCPKeepAlive yes
|
169
|
-
IdentitiesOnly yes
|
170
|
-
|
171
|
-
Host gist
|
172
|
-
User git
|
173
|
-
Port 22
|
174
|
-
Hostname gist.github.com
|
175
|
-
IdentityFile spec/github_rsa
|
176
|
-
TCPKeepAlive yes
|
177
|
-
IdentitiesOnly yes
|
178
|
-
|
179
|
-
Match exec "nmcli connection status id <ap-name> 2> /dev/null"
|
180
|
-
ProxyCommand ssh -W %h:%p github.com
|
181
|
-
OUT
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
context '`keyregen` command' do
|
186
|
-
before do
|
187
|
-
@cli = Sconb::CLI.new
|
188
|
-
allow(@cli).to receive_messages(:stdin_read => <<INN
|
189
|
-
{
|
190
|
-
"github.com": {
|
191
|
-
"Host": "github.com",
|
192
|
-
"User": "git",
|
193
|
-
"Port": "22",
|
194
|
-
"Hostname": "github.com",
|
195
|
-
"IdentityFile": [
|
196
|
-
"/tmp/sconb_spec_github_rsa"
|
197
|
-
],
|
198
|
-
"IdentityFileContent": [
|
199
|
-
"This is github_rsa"
|
200
|
-
]
|
201
|
-
},
|
202
|
-
"gist": {
|
203
|
-
"Host": "gist",
|
204
|
-
"User": "git",
|
205
|
-
"Port": "22",
|
206
|
-
"Hostname": "gist.github.com",
|
207
|
-
"IdentityFile": [
|
208
|
-
"/tmp/sconb_spec_gist_rsa"
|
209
|
-
],
|
210
|
-
"IdentityFileContent": [
|
211
|
-
"This is gist_rsa"
|
212
|
-
]
|
213
|
-
}
|
214
|
-
}
|
215
|
-
INN
|
216
|
-
)
|
217
|
-
end
|
218
|
-
|
219
|
-
it "should generate private keys from JSON to config" do
|
220
|
-
@cli.keyregen
|
221
|
-
expect(File.open('/tmp/sconb_spec_github_rsa').read).to eq "This is github_rsa"
|
222
|
-
expect(File.open('/tmp/sconb_spec_gist_rsa').read).to eq "This is gist_rsa"
|
223
|
-
end
|
224
|
-
|
225
|
-
after do
|
226
|
-
File.unlink('/tmp/sconb_spec_github_rsa')
|
227
|
-
File.unlink('/tmp/sconb_spec_gist_rsa')
|
228
|
-
end
|
229
|
-
|
230
|
-
end
|
231
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
require 'coveralls'
|
2
|
-
Coveralls.wear!
|
3
|
-
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
4
|
-
require 'sconb'
|
5
|
-
|
6
|
-
# this method is written by wycats
|
7
|
-
def capture(stream)
|
8
|
-
begin
|
9
|
-
stream = stream.to_s
|
10
|
-
eval "$#{stream} = StringIO.new"
|
11
|
-
yield
|
12
|
-
result = eval("$#{stream}").string
|
13
|
-
ensure
|
14
|
-
eval("$#{stream} = #{stream.upcase}")
|
15
|
-
end
|
16
|
-
|
17
|
-
result
|
18
|
-
end
|