rake-pro 0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f63cac62f28dd2f2eb0b2eff0fa951ecd5954418
4
+ data.tar.gz: 00d40a32a74cf8825d716f4ebcfd9a23402892f5
5
+ SHA512:
6
+ metadata.gz: fde4d66bf7f5536bad3ca5a01ab282bbe9608fee7b1af773e1013516bb048fcfc8bb33f2abe177702947928a54a60cffa2605b28601a91faa9dd4b120ac15086
7
+ data.tar.gz: 73d4987dae92dc995dd9af00c10178e87a0de3845318091cb6877d7f2721676e72022492fbd5a3bc8b3d5463d3d990de1976d890130d2ab4a276a2103ce0c0c6
@@ -0,0 +1,10 @@
1
+ require "rake/pro/version"
2
+ require "rake/pro/hashex"
3
+ require "rake/pro/ssh_tunnel"
4
+ require "rake/pro/keystore"
5
+
6
+ module Rake
7
+ module Pro
8
+ # Your code goes here...
9
+ end
10
+ end
@@ -0,0 +1,135 @@
1
+ class Hash
2
+ def recursive_merge(new_hash)
3
+ self.merge(new_hash) do |k, old_val, new_val|
4
+ if new_val.respond_to?(:blank) && new_val.blank?
5
+ old_val
6
+ elsif (old_val.kind_of?(Hash) and new_val.kind_of?(Hash))
7
+ old_val.recursive_merge(new_val)
8
+ else
9
+ new_val
10
+ end
11
+ end
12
+ end
13
+
14
+ def recursive_merge!(new_hash)
15
+ self.merge!(new_hash) do |k, old_val, new_val|
16
+ if new_val.respond_to?(:blank) && new_val.blank?
17
+ old_val
18
+ elsif (old_val.kind_of?(Hash) and new_val.kind_of?(Hash))
19
+ old_val.recursive_merge!(new_val)
20
+ else
21
+ new_val
22
+ end
23
+ end
24
+ end
25
+
26
+ def match strings
27
+ select { |key,val|
28
+ is_match = false
29
+ strings.each { |findstr|
30
+ is_match ||= key.downcase.include?(findstr) || val.downcase.include?(findstr)
31
+ }
32
+ is_match
33
+ }
34
+ end
35
+
36
+ def symbolize_keys
37
+ inject({}) { |memo,(k,v)|
38
+ memo[k.to_sym] = v.is_a?(Hash) ? v.symbolize_keys : v;
39
+ memo
40
+ }
41
+ end
42
+
43
+
44
+ # CASE
45
+ # admin@dev,prod:
46
+ # username: abc
47
+ # password
48
+ # returns
49
+ # scopes : [dev, prod]
50
+ # sub_key : admin
51
+ # admin:
52
+ # username: abc
53
+ #
54
+ # CASE:
55
+ # dev:
56
+ # url: abc
57
+ #
58
+ # returns
59
+ # scopes: [dev]
60
+ # subkey: nil
61
+ #
62
+ # CASE:
63
+ # url@dev: abc
64
+ #
65
+ # returns:
66
+ # scopes: [dev]
67
+ # subkey: url
68
+ #
69
+ # CASE:
70
+ # dev:
71
+ # url: abc
72
+ #
73
+
74
+ def key_details(k)
75
+ subkey = scopes = nil
76
+ sk = k.to_s
77
+ skp = sk.split('@')
78
+ subkey = skp.shift.to_sym if (skp.length > 1)
79
+ scopes = skp[0].split(/\s*[&,\|]\s*/)
80
+ [scopes.map { |scope| scope.to_sym }, subkey]
81
+ end
82
+
83
+ def promote_key pk
84
+ pk = pk.to_sym
85
+ coh = {}
86
+ promoted = false
87
+ self.each_pair { |k, v|
88
+ scopes, subkey = key_details(k)
89
+ if (scopes.include?(pk))
90
+ promoted = true
91
+ if subkey.nil?
92
+ v.each_pair { |sk, sv|
93
+ coh[sk] = sv
94
+ }
95
+ coh[k] = v
96
+ else
97
+ coh[subkey] =v
98
+ end
99
+ else
100
+ if v.is_a?(Hash)
101
+ coh[k], subpromo = v.promote_key(pk)
102
+ promoted |= subpromo
103
+ else
104
+ coh[k] = v
105
+ end
106
+ end
107
+ }
108
+ [coh, promoted]
109
+ end
110
+
111
+ def prune_keys pks
112
+ coh = {}
113
+ self.each_pair { |k, v|
114
+ scopes, subkey = key_details(k)
115
+ prune = false
116
+ scopes.each { |scope| prune |= pks.include?(scope) }
117
+ if prune
118
+ scopes.each { |scope|
119
+ if !pks.include?(scope)
120
+ if (subkey.nil?)
121
+ coh[scope] = v
122
+ else
123
+ coh[scope] = {}
124
+ coh[scope][subkey] = v
125
+ end
126
+ end
127
+ }
128
+ else
129
+ coh[k] = v.is_a?(Hash) ? v.prune_keys(pks) : v
130
+ end
131
+ }
132
+ coh
133
+ end
134
+
135
+ end
@@ -0,0 +1,152 @@
1
+ require 'rake'
2
+ require 'yaml'
3
+ require 'etc'
4
+ require_relative './hashex'
5
+
6
+ module Rake
7
+ class Application
8
+ attr_accessor :cfg
9
+ attr_accessor :scopes
10
+ attr_accessor :active_dir
11
+
12
+ class KeySpace
13
+ def initialize
14
+ @cfg_files = []
15
+ @kvp_stack = []
16
+ @kvp = {}
17
+ end
18
+
19
+ def [](key)
20
+ @kvp[key]
21
+ end
22
+
23
+ def values()
24
+ @kvp
25
+ end
26
+
27
+ def home()
28
+ Etc.getpwuid.dir;
29
+ end
30
+
31
+ def root()
32
+ Rake.original_dir
33
+ end
34
+
35
+ def rpath(*paths)
36
+ return File.join(paths)
37
+ end
38
+
39
+ def push_scope scope
40
+ (@scopes ||= []).push(scope)
41
+ end
42
+
43
+ def pop_scope
44
+ @scopes.pop
45
+ end
46
+
47
+ def push_cfg cfg_file
48
+ #puts "Loading cfg file: #{cfg_file}..."
49
+ cfg = YAML.load_file(cfg_file).symbolize_keys
50
+ source = if (cfg.has_key?(:default))
51
+ cfg[:default]
52
+ elsif (cfg.has_key?(:source))
53
+ cfg[:source]
54
+ elsif (cfg.has_key?(:system))
55
+ cfg[:system]
56
+ elsif(cfg.has_key?(:target))
57
+ cfg[:target]
58
+ end
59
+
60
+ push_scope(source.to_sym) if source
61
+ @kvp_stack.push(cfg)
62
+ end
63
+
64
+ def pop_cfg
65
+ @kvp_stack.pop
66
+ end
67
+
68
+ def load_once(cfg_file)
69
+ unless @cfg_files.include?(cfg_file)
70
+ if (File.file?(cfg_file))
71
+ Rake.application.active_dir = File.dirname(cfg_file)
72
+ push_cfg(cfg_file)
73
+ end
74
+ @cfg_files.push(cfg_file)
75
+ end
76
+ end
77
+
78
+ def scope_siblings scope
79
+ siblings = nil
80
+ Rake.application.scopes.each { |scope_set|
81
+ siblings = scope_set if scope_set.include?(scope)
82
+ }
83
+ siblings
84
+ end
85
+
86
+ def before_invoke task_details
87
+ task_name = task_details[0]
88
+ parts = task_name.split(':')
89
+ push_scope(parts[0].to_sym) if parts.length == 1
90
+
91
+ load_paths = parts.reduce([
92
+ rpath(root, 'cfg.yml'),
93
+ rpath(root, '.cfg.yml'),
94
+ rpath(root, '.cfg-private.yml')
95
+ ]) { |paths, folder|
96
+ paths.push(rpath(root, folder, 'cfg.yml'))
97
+ paths.push(rpath(root, folder, '.cfg.yml'))
98
+ paths.push(rpath(root, folder, '.cfg-private.yml'))
99
+ paths
100
+ }
101
+ load_paths.push(rpath(home, '.cyborg.yml'))
102
+
103
+ load_paths.each { |path|
104
+ load_once(path)
105
+ }
106
+
107
+ # promote and prune the key space
108
+ pruned = false
109
+ pruned_stack = []
110
+ @kvp_stack.each_with_index { |kvp, index|
111
+ @scopes.each_with_index { |scope, i2|
112
+ kvp, didprune = kvp.promote_key(scope)
113
+ siblings = scope_siblings(scope)
114
+ kvp = kvp.prune_keys(scope_siblings(scope)) if siblings
115
+ pruned |= (didprune && scope == task_name.to_sym)
116
+ }
117
+ pruned_stack.push(kvp)
118
+ }
119
+
120
+ # merge the kvp stack entries into a single map
121
+ @kvp = pruned_stack.reduce({}) { |acc, kvp|
122
+ acc.recursive_merge(kvp)
123
+ }
124
+
125
+ [task_name, pruned]
126
+ end
127
+
128
+ def after_invoke task_details
129
+ task_name = task_details[0]
130
+ parts = task_name.split(':')
131
+ if parts.length > 1
132
+ pop_scope
133
+ pop_cfg
134
+ end
135
+ end
136
+
137
+ end
138
+ end
139
+
140
+ module Intercept
141
+ def invoke_task *args
142
+ task_name, pruned = (Rake.application.cfg ||= Application::KeySpace.new).before_invoke(parse_task_string args.first)
143
+ super if Rake::Task.task_defined?(task_name) || !pruned
144
+ Rake.application.cfg.after_invoke(parse_task_string args.first)
145
+ end
146
+ end
147
+
148
+ end
149
+
150
+ Rake::Application.class_eval do
151
+ prepend Rake::Intercept
152
+ end
@@ -0,0 +1,90 @@
1
+ require 'open4'
2
+
3
+ module Rake
4
+
5
+ class CommandNotFound < StandardError
6
+ end
7
+
8
+ class AbnormalExitStatus < StandardError
9
+ attr_reader :exit_status
10
+ def initialize(exit_status, error_lines)
11
+ @exit_status = exit_status
12
+ super error_lines
13
+ end
14
+ end
15
+
16
+ class Local
17
+
18
+ def sh command_line, opts = {}
19
+ native_output_only = command_line.include?('--terse') || opts[:terse]
20
+ if native_output_only
21
+ command_line.sub!(' --terse', '')
22
+ opts[:echo] = true
23
+ opts[:echo_cmd] = false
24
+ end
25
+ echo_command_output = opts[:echo] || true
26
+ command_line = "#{command_line}"
27
+ puts "$ #{command_line}"
28
+ command_output = ""
29
+ status = Open4::popen4(command_line) do |pid, stdin, stdout, stderr|
30
+ command_output = capture_output(stdout, stderr, echo_command_output, native_output_only)
31
+ end
32
+ if status.exitstatus != 0
33
+ raise AbnormalExitStatus.new(status.exitstatus, command_output) if opts[:raise_on_error]
34
+ end
35
+ command_output.strip
36
+ rescue AbnormalExitStatus
37
+ raise
38
+ rescue Errno::ENOENT
39
+ raise CommandNotFound, "Bash Error. Command or file arguments not found." if opts[:raise_on_error]
40
+ end
41
+
42
+ def capture_output stdout, stderr, echo_command_output, native_output_only
43
+ stdout_lines = ""
44
+ stderr_lines = ""
45
+ command_output = ""
46
+ loop do
47
+ begin
48
+ # check whether stdout, stderr or both are
49
+ # ready to be read from without blocking
50
+ IO.select([stdout,stderr]).flatten.compact.each { |io|
51
+ # stdout, if ready, goes to stdout_lines
52
+ stdout_lines += io.readpartial(1024) if io.fileno == stdout.fileno
53
+ # stderr, if ready, goes to stdout_lines
54
+ stderr_lines += io.readpartial(1024) if io.fileno == stderr.fileno
55
+ }
56
+ break if stdout.closed? && stderr.closed?
57
+ rescue EOFError
58
+ # Note, readpartial triggers the EOFError too soon. Continue to flush the
59
+ # pending io (via readpartial) until we have received all characters
60
+ # out from the IO socket.
61
+ break if stdout_lines.length == 0 && stderr_lines.length == 0
62
+ ensure
63
+ # if we acumulated any complete lines (\n-terminated)
64
+ # in either stdout/err_lines, output them now
65
+ stdout_lines.sub!(/.*\n/) {
66
+ command_output << $&
67
+ if echo_command_output
68
+ if native_output_only
69
+ puts $&.strip
70
+ else
71
+ puts $&.strip
72
+ end
73
+ end
74
+ }
75
+ stderr_lines.sub!(/.*\n/) {
76
+ command_output << $&
77
+ if echo_command_output
78
+ if native_output_only
79
+ puts $&.strip
80
+ else
81
+ puts $&.strip
82
+ end
83
+ end
84
+ }
85
+ end
86
+ end
87
+ command_output
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,109 @@
1
+ require 'openssl'
2
+ require 'net/ssh/gateway'
3
+ require 'open4'
4
+
5
+ module Rake
6
+
7
+ class Local
8
+
9
+ def sh command_line, opts = {}
10
+ native_output_only = command_line.include?('--terse') || opts[:terse]
11
+ if native_output_only
12
+ command_line.sub!(' --terse', '')
13
+ opts[:echo] = true
14
+ opts[:echo_cmd] = false
15
+ end
16
+ echo_command_output = opts[:echo] || true
17
+ command_line = "#{command_line}"
18
+ puts "$ #{command_line}"
19
+ command_output = ""
20
+ status = Open4::popen4(command_line) do |pid, stdin, stdout, stderr|
21
+ command_output = capture_output(stdout, stderr, echo_command_output, native_output_only)
22
+ end
23
+ if status.exitstatus != 0
24
+ raise AbnormalExitStatus.new(status.exitstatus, command_output) if opts[:raise_on_error]
25
+ end
26
+ command_output.strip
27
+ rescue AbnormalExitStatus
28
+ raise
29
+ rescue Errno::ENOENT
30
+ raise CommandNotFound, "Bash Error. Command or file arguments not found." if opts[:raise_on_error]
31
+ end
32
+
33
+ def capture_output stdout, stderr, echo_command_output, native_output_only
34
+ stdout_lines = ""
35
+ stderr_lines = ""
36
+ command_output = ""
37
+ loop do
38
+ begin
39
+ # check whether stdout, stderr or both are
40
+ # ready to be read from without blocking
41
+ IO.select([stdout,stderr]).flatten.compact.each { |io|
42
+ # stdout, if ready, goes to stdout_lines
43
+ stdout_lines += io.readpartial(1024) if io.fileno == stdout.fileno
44
+ # stderr, if ready, goes to stdout_lines
45
+ stderr_lines += io.readpartial(1024) if io.fileno == stderr.fileno
46
+ }
47
+ break if stdout.closed? && stderr.closed?
48
+ rescue EOFError
49
+ # Note, readpartial triggers the EOFError too soon. Continue to flush the
50
+ # pending io (via readpartial) until we have received all characters
51
+ # out from the IO socket.
52
+ break if stdout_lines.length == 0 && stderr_lines.length == 0
53
+ ensure
54
+ # if we acumulated any complete lines (\n-terminated)
55
+ # in either stdout/err_lines, output them now
56
+ stdout_lines.sub!(/.*\n/) {
57
+ command_output << $&
58
+ if echo_command_output
59
+ if native_output_only
60
+ puts $&.strip
61
+ else
62
+ puts $&.strip
63
+ end
64
+ end
65
+ }
66
+ stderr_lines.sub!(/.*\n/) {
67
+ command_output << $&
68
+ if echo_command_output
69
+ if native_output_only
70
+ puts $&.strip
71
+ else
72
+ puts $&.strip
73
+ end
74
+ end
75
+ }
76
+ end
77
+ end
78
+ command_output
79
+ end
80
+ end
81
+
82
+ class SSH
83
+ class << self
84
+ def tunnel
85
+ host = gateway = port = jump = nil
86
+ cfg = Rake.application.cfg.values
87
+ if cfg.has_key?(:jumpbox)
88
+ jump = cfg[:jumpbox]
89
+ gateway = Net::SSH::Gateway.new(
90
+ jump[:host], jump[:user],
91
+ :keys => [jump[:keyfile]] #, :verbose => :debug
92
+ )
93
+ host = "127.0.0.1"
94
+ port = gateway.open(cfg[:host], cfg[:port], jump[:port])
95
+ else
96
+ host = cfg[:host]
97
+ port = cfg[:port]
98
+ end
99
+ local = Rake::Local.new
100
+ yield(local, host, port) if block_given?
101
+ rescue Net::SSH::AuthenticationFailed => ex
102
+ puts "\nError: SSH Failed to Authenticate. You may need to run\n $ ssh-add ~/.ssh/#{jump[:keyfile]} # key file\n ** or add this line to your .bashrc.\n\n"
103
+ ensure
104
+ gateway.close(port) unless gateway.nil?
105
+ end
106
+ end
107
+ end
108
+
109
+ end
@@ -0,0 +1,5 @@
1
+ module Rake
2
+ module Pro
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,135 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rake-pro
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dean Hallman
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-03-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: open4
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: openssl
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: net-ssh-gateway
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.0'
97
+ description: Adds support for key/value store with template expansion, ssh tunneling,
98
+ local and remote shell improvements etc.
99
+ email:
100
+ - rdhallman@gmail.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - lib/rake/pro.rb
106
+ - lib/rake/pro/hashex.rb
107
+ - lib/rake/pro/keystore.rb
108
+ - lib/rake/pro/localsh.rb
109
+ - lib/rake/pro/ssh_tunnel.rb
110
+ - lib/rake/pro/version.rb
111
+ homepage: http://www.github.com/
112
+ licenses:
113
+ - MIT
114
+ metadata: {}
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubyforge_project:
131
+ rubygems_version: 2.6.13
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: Enterprise extensions for Rake.
135
+ test_files: []