balmora 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/balmora +6 -0
- data/lib/balmora.rb +77 -0
- data/lib/balmora/arguments.rb +175 -0
- data/lib/balmora/cli.rb +150 -0
- data/lib/balmora/command.rb +106 -0
- data/lib/balmora/command/commands.rb +21 -0
- data/lib/balmora/command/exec.rb +24 -0
- data/lib/balmora/command/file.rb +168 -0
- data/lib/balmora/command/files.rb +122 -0
- data/lib/balmora/command/pacman.rb +71 -0
- data/lib/balmora/command/reload_config.rb +13 -0
- data/lib/balmora/command/restart.rb +13 -0
- data/lib/balmora/command/set_variable.rb +41 -0
- data/lib/balmora/command/stop.rb +17 -0
- data/lib/balmora/command/yaourt.rb +8 -0
- data/lib/balmora/config.rb +190 -0
- data/lib/balmora/context.rb +98 -0
- data/lib/balmora/context/config_changed.rb +11 -0
- data/lib/balmora/context/exec.rb +26 -0
- data/lib/balmora/context/exec_result.rb +7 -0
- data/lib/balmora/contexts.rb +76 -0
- data/lib/balmora/extension.rb +58 -0
- data/lib/balmora/extension/file_secret.rb +45 -0
- data/lib/balmora/logger.rb +25 -0
- data/lib/balmora/require.rb +25 -0
- data/lib/balmora/shell.rb +118 -0
- data/lib/balmora/state.rb +82 -0
- data/lib/balmora/variables.rb +110 -0
- data/lib/balmora/variables/config.rb +7 -0
- data/lib/balmora/variables/variables.rb +7 -0
- metadata +74 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
module Balmora::Extension::FileSecret
|
2
|
+
|
3
|
+
def options()
|
4
|
+
return super() + [:password]
|
5
|
+
end
|
6
|
+
|
7
|
+
def _copy_file()
|
8
|
+
if @action == 'pull'
|
9
|
+
@shell.run!(_source_contents() + [_expr('|'), *@shell.sudo(), 'tee',
|
10
|
+
_target_path()])
|
11
|
+
else
|
12
|
+
@shell.run!(['cat', _source_path(), _expr('|'), *_encrypt(), _expr('|'),
|
13
|
+
*@shell.sudo(), 'tee', _target_path()])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def _source_contents()
|
18
|
+
command = super()
|
19
|
+
|
20
|
+
if @action == 'pull'
|
21
|
+
command += [_expr('|')] + _decrypt()
|
22
|
+
end
|
23
|
+
|
24
|
+
return command
|
25
|
+
end
|
26
|
+
|
27
|
+
def _target_contents()
|
28
|
+
command = super()
|
29
|
+
|
30
|
+
if @action == 'push'
|
31
|
+
command += [_expr('|')] + _decrypt()
|
32
|
+
end
|
33
|
+
|
34
|
+
return command
|
35
|
+
end
|
36
|
+
|
37
|
+
def _decrypt()
|
38
|
+
return ['openssl', 'enc', '-aes-256-cbc', '-d', '-pass', option(:password)]
|
39
|
+
end
|
40
|
+
|
41
|
+
def _encrypt()
|
42
|
+
return ['openssl', 'enc', '-aes-256-cbc', '-e', '-pass', option(:password)]
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
class Balmora::Logger < Logger
|
4
|
+
|
5
|
+
def self.factory(state)
|
6
|
+
logger = self.new(STDOUT)
|
7
|
+
|
8
|
+
if state.options[:debug] == true && state.options[:quite] == true
|
9
|
+
raise Error.new("Options --quite and --verbose can not be set " +
|
10
|
+
"simulataneously")
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
if state.options[:verbose] == true
|
15
|
+
logger.level = ::Logger::DEBUG
|
16
|
+
elsif state.options[:quiet] != true
|
17
|
+
logger.level = ::Logger::INFO
|
18
|
+
else
|
19
|
+
logger.level = ::Logger::ERROR
|
20
|
+
end
|
21
|
+
|
22
|
+
return logger
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'balmora/logger'
|
2
|
+
require 'balmora/extension'
|
3
|
+
require 'balmora/config'
|
4
|
+
require 'balmora/shell'
|
5
|
+
require 'balmora/variables'
|
6
|
+
require 'balmora/command'
|
7
|
+
require 'balmora/contexts'
|
8
|
+
require 'balmora/context'
|
9
|
+
|
10
|
+
require 'balmora/state'
|
11
|
+
|
12
|
+
globs = [
|
13
|
+
'extension/**/*.rb',
|
14
|
+
'command/**/*.rb',
|
15
|
+
'variables/**/*.rb',
|
16
|
+
'context/**/*.rb'
|
17
|
+
]
|
18
|
+
|
19
|
+
Dir.chdir(File.dirname(__FILE__)) {
|
20
|
+
globs.each() { |glob|
|
21
|
+
Dir.glob(glob).each() { |file|
|
22
|
+
require ::File.join('balmora', file)
|
23
|
+
}
|
24
|
+
}
|
25
|
+
}
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
class Balmora::Shell
|
4
|
+
|
5
|
+
class Error < StandardError
|
6
|
+
|
7
|
+
attr_reader :status
|
8
|
+
|
9
|
+
def initialize(message, status)
|
10
|
+
super(message)
|
11
|
+
@status = status
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
class Expression
|
17
|
+
|
18
|
+
attr_reader :command
|
19
|
+
|
20
|
+
def initialize(command)
|
21
|
+
@command = command
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_accessor :exec, :status
|
27
|
+
attr_reader :home, :user_id
|
28
|
+
|
29
|
+
def self.factory(state)
|
30
|
+
return self.new(state.logger)
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(logger, home = nil)
|
34
|
+
@logger = logger
|
35
|
+
@run = ::Object.method(:'`')
|
36
|
+
@system = ::Object.method(:system)
|
37
|
+
@status = Proc.new() { $?.exitstatus }
|
38
|
+
@home = Dir.home()
|
39
|
+
@user_id ||= `id -un; id -gn`.strip().split("\n").join(':')
|
40
|
+
@sudo_stack = []
|
41
|
+
end
|
42
|
+
|
43
|
+
def expression(command)
|
44
|
+
Expression.new(command)
|
45
|
+
end
|
46
|
+
|
47
|
+
def sudo()
|
48
|
+
if @sudo_stack.length == 0
|
49
|
+
return []
|
50
|
+
end
|
51
|
+
|
52
|
+
if @sudo_stack[-1] == true
|
53
|
+
return ['sudo']
|
54
|
+
else
|
55
|
+
return ['sudo', '--user', @sudo_stack[-1]]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def run(command, options = {})
|
60
|
+
command = sudo() + command
|
61
|
+
|
62
|
+
shell_command =
|
63
|
+
command.
|
64
|
+
collect() { |part|
|
65
|
+
if part.instance_of?(Expression)
|
66
|
+
next part.command
|
67
|
+
end
|
68
|
+
|
69
|
+
next Shellwords.escape(part)
|
70
|
+
}.
|
71
|
+
join(' ')
|
72
|
+
|
73
|
+
if options[:verbose] != false
|
74
|
+
@logger.info(shell_command)
|
75
|
+
else
|
76
|
+
@logger.debug(shell_command)
|
77
|
+
end
|
78
|
+
|
79
|
+
method = @run
|
80
|
+
if options[:system] == true
|
81
|
+
method = @system
|
82
|
+
end
|
83
|
+
|
84
|
+
result = method.call(shell_command)
|
85
|
+
status = @status.call()
|
86
|
+
|
87
|
+
if options[:raise] == true && status != 0
|
88
|
+
raise Error.new("Failed to execute command: #{shell_command}",
|
89
|
+
status)
|
90
|
+
end
|
91
|
+
|
92
|
+
return status, result
|
93
|
+
end
|
94
|
+
|
95
|
+
def run!(command, options = {})
|
96
|
+
return run(command, options.merge(raise: true))[1]
|
97
|
+
end
|
98
|
+
|
99
|
+
def system(command, options = {})
|
100
|
+
return run(command, options.merge(raise: true, system: true))[1]
|
101
|
+
end
|
102
|
+
|
103
|
+
def expand(path)
|
104
|
+
if path.start_with?('~') || path.start_with?('/')
|
105
|
+
return ::File.expand_path(path)
|
106
|
+
end
|
107
|
+
|
108
|
+
return path
|
109
|
+
end
|
110
|
+
|
111
|
+
def sudo!(sudo)
|
112
|
+
@sudo_stack.push(sudo)
|
113
|
+
yield
|
114
|
+
ensure
|
115
|
+
@sudo_stack.pop()
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
class Balmora::State
|
2
|
+
|
3
|
+
attr_reader :options, :arguments, :logger, :balmora, :extension, :config
|
4
|
+
attr_reader :shell, :variables, :context
|
5
|
+
|
6
|
+
@@constants = [
|
7
|
+
Balmora::Logger,
|
8
|
+
Balmora::Extension,
|
9
|
+
Balmora::Shell,
|
10
|
+
Balmora::Variables,
|
11
|
+
Balmora::Config,
|
12
|
+
Balmora::Contexts,
|
13
|
+
Balmora,
|
14
|
+
]
|
15
|
+
|
16
|
+
def self.create(options, arguments)
|
17
|
+
state = Balmora::State.new()
|
18
|
+
state.instance_variable_set(:@options, options)
|
19
|
+
state.instance_variable_set(:@arguments, arguments)
|
20
|
+
|
21
|
+
config = Balmora::Config.create(options[:config])
|
22
|
+
config.load()
|
23
|
+
|
24
|
+
state.instance_variable_set(:@config, config)
|
25
|
+
|
26
|
+
constants = @@constants.clone()
|
27
|
+
Balmora.constants.each() { |constant|
|
28
|
+
if !constants.include?(constant)
|
29
|
+
constants.push(constant)
|
30
|
+
end
|
31
|
+
}
|
32
|
+
|
33
|
+
constants.delete(Balmora::Config)
|
34
|
+
|
35
|
+
constants.each() { |constant|
|
36
|
+
if !constant.respond_to?(:factory)
|
37
|
+
next
|
38
|
+
end
|
39
|
+
|
40
|
+
name =
|
41
|
+
constant.
|
42
|
+
to_s().
|
43
|
+
split('::')[1..-1].
|
44
|
+
join('_').
|
45
|
+
split(/(?=[A-Z])/).
|
46
|
+
collect() { |word| word.downcase() }.
|
47
|
+
join('_').
|
48
|
+
to_sym()
|
49
|
+
|
50
|
+
if name == :''
|
51
|
+
name = :balmora
|
52
|
+
end
|
53
|
+
|
54
|
+
state.set(name, constant.factory(state))
|
55
|
+
}
|
56
|
+
|
57
|
+
config.variables = state.variables
|
58
|
+
|
59
|
+
return state
|
60
|
+
end
|
61
|
+
|
62
|
+
def set(name, instance)
|
63
|
+
if self.instance_variable_defined?(:"@#{name}")
|
64
|
+
raise Error.new("Can not set #{name.inspect()}: variable is already set")
|
65
|
+
end
|
66
|
+
|
67
|
+
self.instance_variable_set(:"@#{name}", instance)
|
68
|
+
end
|
69
|
+
|
70
|
+
def method_missing(method, *arguments)
|
71
|
+
if arguments.length > 1
|
72
|
+
super(method, *arguments)
|
73
|
+
end
|
74
|
+
|
75
|
+
if !self.instance_variable_defined?(:"@#{method}")
|
76
|
+
super(method, *arguments)
|
77
|
+
end
|
78
|
+
|
79
|
+
return self.instance_variable_get(:"@#{method}")
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
class Balmora::Variables
|
2
|
+
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
def self.factory(state)
|
6
|
+
return self.new(state.extension, state.shell, state)
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(extension, shell, state)
|
10
|
+
@extension = extension
|
11
|
+
@shell = shell
|
12
|
+
@state = state
|
13
|
+
end
|
14
|
+
|
15
|
+
def get(name)
|
16
|
+
if !name.instance_of?(::Array)
|
17
|
+
name = name.split('.').collect() { |part| part.to_sym() }
|
18
|
+
else
|
19
|
+
name = name.clone()
|
20
|
+
end
|
21
|
+
|
22
|
+
parts = []
|
23
|
+
provider = @extension.get(Balmora::Variables, name.shift()).factory(@state)
|
24
|
+
|
25
|
+
result =
|
26
|
+
name.
|
27
|
+
inject(provider) { |current, part|
|
28
|
+
parts.push(part)
|
29
|
+
|
30
|
+
if current.instance_of?(::Hash)
|
31
|
+
if !current.has_key?(part)
|
32
|
+
raise Error.new("Unknown variable #{parts.join('.')}")
|
33
|
+
end
|
34
|
+
|
35
|
+
next current[part]
|
36
|
+
end
|
37
|
+
|
38
|
+
if !current.respond_to?(part)
|
39
|
+
raise Error.new("Unknown variable #{parts.join('.')}")
|
40
|
+
end
|
41
|
+
|
42
|
+
next current.public_send(part)
|
43
|
+
}
|
44
|
+
|
45
|
+
return result
|
46
|
+
end
|
47
|
+
|
48
|
+
def inject(value, string = true)
|
49
|
+
result =
|
50
|
+
if value.instance_of?(::Array)
|
51
|
+
_inject_array(value, string)
|
52
|
+
elsif value.instance_of?(::Hash)
|
53
|
+
_inject_hash(value, string)
|
54
|
+
elsif value.instance_of?(::String) && string
|
55
|
+
_inject_string(value)
|
56
|
+
else
|
57
|
+
value
|
58
|
+
end
|
59
|
+
|
60
|
+
return result
|
61
|
+
end
|
62
|
+
|
63
|
+
def _inject_array(value, string)
|
64
|
+
result = []
|
65
|
+
|
66
|
+
value.each_with_index() { |item, index|
|
67
|
+
if item.instance_of?(::Hash) && item.keys() == [:'extend-variable']
|
68
|
+
result += get(item[:'extend-variable'])
|
69
|
+
else
|
70
|
+
result.push(inject(item, string))
|
71
|
+
end
|
72
|
+
}
|
73
|
+
|
74
|
+
return result
|
75
|
+
end
|
76
|
+
|
77
|
+
def _inject_hash(value, string)
|
78
|
+
result = {}
|
79
|
+
value.each() { |key, item|
|
80
|
+
if key == :'include-variable'
|
81
|
+
result.merge!(get(item))
|
82
|
+
else
|
83
|
+
result[key] = inject(item, string)
|
84
|
+
end
|
85
|
+
}
|
86
|
+
|
87
|
+
return result
|
88
|
+
end
|
89
|
+
|
90
|
+
def _inject_string(value)
|
91
|
+
value = value.gsub('\\\\', '{{__ESCAPED_SLASH__}}')
|
92
|
+
|
93
|
+
if value.match(/(?<!\\)\#\{/)
|
94
|
+
value = value.gsub('\\#', '#')
|
95
|
+
value = eval('"' + value + '"')
|
96
|
+
end
|
97
|
+
|
98
|
+
value = value.gsub(/(?<!\\)\${(.*?)}/) { |string | get(string[2...-1]) }
|
99
|
+
value = value.gsub(/(?<!\\)\%{(.*?)}/) { |string |
|
100
|
+
command = @shell.expression(string[2...-1])
|
101
|
+
@shell.run!([command], verbose: false, message: 'Executing embedded ' +
|
102
|
+
'command: ').rstrip()
|
103
|
+
}
|
104
|
+
|
105
|
+
value = value.gsub('\\$', '$')
|
106
|
+
value = value.gsub('\\%', '%')
|
107
|
+
value = value.gsub('{{__ESCAPED_SLASH__}}', '\\')
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: balmora
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Leonid Shagabutdinov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-20 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Balmora - linux task runner
|
14
|
+
email: leonid@shagabutdinov.com
|
15
|
+
executables:
|
16
|
+
- balmora
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- bin/balmora
|
21
|
+
- lib/balmora.rb
|
22
|
+
- lib/balmora/arguments.rb
|
23
|
+
- lib/balmora/cli.rb
|
24
|
+
- lib/balmora/command.rb
|
25
|
+
- lib/balmora/command/commands.rb
|
26
|
+
- lib/balmora/command/exec.rb
|
27
|
+
- lib/balmora/command/file.rb
|
28
|
+
- lib/balmora/command/files.rb
|
29
|
+
- lib/balmora/command/pacman.rb
|
30
|
+
- lib/balmora/command/reload_config.rb
|
31
|
+
- lib/balmora/command/restart.rb
|
32
|
+
- lib/balmora/command/set_variable.rb
|
33
|
+
- lib/balmora/command/stop.rb
|
34
|
+
- lib/balmora/command/yaourt.rb
|
35
|
+
- lib/balmora/config.rb
|
36
|
+
- lib/balmora/context.rb
|
37
|
+
- lib/balmora/context/config_changed.rb
|
38
|
+
- lib/balmora/context/exec.rb
|
39
|
+
- lib/balmora/context/exec_result.rb
|
40
|
+
- lib/balmora/contexts.rb
|
41
|
+
- lib/balmora/extension.rb
|
42
|
+
- lib/balmora/extension/file_secret.rb
|
43
|
+
- lib/balmora/logger.rb
|
44
|
+
- lib/balmora/require.rb
|
45
|
+
- lib/balmora/shell.rb
|
46
|
+
- lib/balmora/state.rb
|
47
|
+
- lib/balmora/variables.rb
|
48
|
+
- lib/balmora/variables/config.rb
|
49
|
+
- lib/balmora/variables/variables.rb
|
50
|
+
homepage: http://github.com/shagabutdinov/balmora
|
51
|
+
licenses:
|
52
|
+
- MIT
|
53
|
+
metadata: {}
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options: []
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
requirements: []
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 2.4.5
|
71
|
+
signing_key:
|
72
|
+
specification_version: 4
|
73
|
+
summary: Balmora
|
74
|
+
test_files: []
|