envo 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/envo-install +18 -0
- data/bin/envo_gen_tmp_helper.rb +50 -0
- data/bin/envo_run +30 -0
- data/lib/envo.rb +45 -0
- data/lib/envo/cli/envo.bat +11 -0
- data/lib/envo/cli/envo.sh +6 -0
- data/lib/envo/cli/help.rb +20 -0
- data/lib/envo/cli/installer_bash.rb +131 -0
- data/lib/envo/cli/installer_win_cmd.rb +96 -0
- data/lib/envo/cli/runner.rb +129 -0
- data/lib/envo/cli_parser.rb +54 -0
- data/lib/envo/cmd_clean.rb +53 -0
- data/lib/envo/cmd_copy.rb +56 -0
- data/lib/envo/cmd_list.rb +50 -0
- data/lib/envo/cmd_list_add.rb +102 -0
- data/lib/envo/cmd_list_del.rb +78 -0
- data/lib/envo/cmd_path.rb +28 -0
- data/lib/envo/cmd_reset.rb +66 -0
- data/lib/envo/cmd_run.rb +68 -0
- data/lib/envo/cmd_set.rb +76 -0
- data/lib/envo/cmd_show.rb +82 -0
- data/lib/envo/cmd_swap.rb +49 -0
- data/lib/envo/cmd_unset.rb +45 -0
- data/lib/envo/context.rb +156 -0
- data/lib/envo/error.rb +7 -0
- data/lib/envo/host.rb +24 -0
- data/lib/envo/host_shell.rb +11 -0
- data/lib/envo/logger.rb +27 -0
- data/lib/envo/parse_result.rb +9 -0
- data/lib/envo/parsed_cmd.rb +10 -0
- data/lib/envo/script_parser.rb +58 -0
- data/lib/envo/shell/bash.rb +63 -0
- data/lib/envo/shell/win_cmd.rb +47 -0
- data/lib/envo/state.rb +71 -0
- data/lib/envo/val/list_val.rb +107 -0
- data/lib/envo/val/no_val.rb +35 -0
- data/lib/envo/val/path_list_val.rb +24 -0
- data/lib/envo/val/path_val.rb +41 -0
- data/lib/envo/val/string_val.rb +40 -0
- data/lib/envo/val/val_builder.rb +59 -0
- data/lib/envo/version.rb +4 -0
- metadata +89 -0
data/lib/envo/error.rb
ADDED
data/lib/envo/host.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Envo
|
2
|
+
class Host
|
3
|
+
def initialize(shell)
|
4
|
+
@shell = shell
|
5
|
+
end
|
6
|
+
attr_reader :shell
|
7
|
+
|
8
|
+
def env
|
9
|
+
ENV
|
10
|
+
end
|
11
|
+
|
12
|
+
def pwd
|
13
|
+
Dir.pwd
|
14
|
+
end
|
15
|
+
|
16
|
+
def home
|
17
|
+
Dir.home
|
18
|
+
end
|
19
|
+
|
20
|
+
def path_exists?(path)
|
21
|
+
File.exist?(path)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Envo
|
2
|
+
HostShell = -> {
|
3
|
+
env_shell = ENV['SHELL']
|
4
|
+
break Shell::WinCmd if !env_shell
|
5
|
+
if env_shell =~ /bash/
|
6
|
+
raise Error.new "bash on Windows (msys) is not supported yet" if env_shell =~ /^[a-zA-Z]\:/
|
7
|
+
break Shell::Bash
|
8
|
+
end
|
9
|
+
raise Error.new "Unknown shell! Please report on https://github.com/iboB/envo/issues"
|
10
|
+
}.()
|
11
|
+
end
|
data/lib/envo/logger.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Envo
|
2
|
+
class Logger
|
3
|
+
ERROR = 0
|
4
|
+
WARN = 1
|
5
|
+
INFO = 2
|
6
|
+
DEBUG = 3
|
7
|
+
def initialize()
|
8
|
+
@max_level = DEBUG
|
9
|
+
end
|
10
|
+
attr_accessor :max_level
|
11
|
+
def log(level, text)
|
12
|
+
return if level > max_level
|
13
|
+
stream = level == 0 ? STDERR : STDOUT
|
14
|
+
stream.puts(text)
|
15
|
+
end
|
16
|
+
def plog(level, text)
|
17
|
+
return if level > max_level
|
18
|
+
stream = level == 0 ? STDERR : STDOUT
|
19
|
+
stream.print(text)
|
20
|
+
end
|
21
|
+
def error(text); log(ERROR, text); end
|
22
|
+
def warn(text); log(WARN, text); end
|
23
|
+
def puts(text); log(INFO, text); end
|
24
|
+
def print(text); plog(INFO, text); end
|
25
|
+
def debug(text); log(DEBUG, text); end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module Envo
|
4
|
+
class ScriptParser
|
5
|
+
def initialize(opts)
|
6
|
+
@known_cmds = {}
|
7
|
+
@known_opts = opts
|
8
|
+
end
|
9
|
+
def add_cmd(name, parse_func)
|
10
|
+
raise Envo::Error "cmd #{name} is already added to parser" if @known_cmds[name]
|
11
|
+
@known_cmds[name] = parse_func
|
12
|
+
end
|
13
|
+
def parse(lines)
|
14
|
+
result = ParseResult.new
|
15
|
+
|
16
|
+
lines.each_with_index do |line, li|
|
17
|
+
li += 1
|
18
|
+
line.strip!
|
19
|
+
next if line.empty?
|
20
|
+
next if line[0] == '#' # comment
|
21
|
+
|
22
|
+
line_opts = if line[0] == '{' # opts pack
|
23
|
+
i = line.index('}')
|
24
|
+
raise Envo::Error.new "#{li}: malformed options pack" if !i
|
25
|
+
opts = line[1...i].split(',')
|
26
|
+
line = line[i+1..]
|
27
|
+
opts
|
28
|
+
else
|
29
|
+
[]
|
30
|
+
end
|
31
|
+
|
32
|
+
raise Envo::Error.new "#{li}: missing command" if line.empty?
|
33
|
+
|
34
|
+
tokens = []
|
35
|
+
begin
|
36
|
+
tokens = CSV::parse_line(line, col_sep: ' ').compact
|
37
|
+
rescue
|
38
|
+
puts "AAAAAAA: #{line.inspect}"
|
39
|
+
raise Envo::Error.new "#{li}: malformed line"
|
40
|
+
end
|
41
|
+
|
42
|
+
raise Envo::Error.new "#{li}: missing command" if tokens.empty?
|
43
|
+
cmd = tokens.shift
|
44
|
+
raise Envo::Error.new "#{li}: unknown command '#{cmd}'" if !@known_cmds[cmd]
|
45
|
+
parsed_cmd = @known_cmds[cmd].(cmd, tokens, line_opts)
|
46
|
+
|
47
|
+
cmd_opts = {}
|
48
|
+
parsed_cmd.opts.each do |opt|
|
49
|
+
cmd_opts.merge! @known_opts.parse_script(opt)
|
50
|
+
end
|
51
|
+
parsed_cmd.opts = cmd_opts
|
52
|
+
|
53
|
+
result.cmds << parsed_cmd
|
54
|
+
end
|
55
|
+
result
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Envo
|
2
|
+
module Shell
|
3
|
+
module Bash
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def installer
|
7
|
+
Cli::InstallerBash
|
8
|
+
end
|
9
|
+
|
10
|
+
def path_var_name
|
11
|
+
'PATH'
|
12
|
+
end
|
13
|
+
def home_var_name
|
14
|
+
'HOME'
|
15
|
+
end
|
16
|
+
|
17
|
+
def likely_abs_path?(val)
|
18
|
+
!val.empty? && val[0] == '/'
|
19
|
+
end
|
20
|
+
def likely_rel_path?(val)
|
21
|
+
return !val.empty? && val[0] == '.'
|
22
|
+
end
|
23
|
+
def fix_path(path)
|
24
|
+
path
|
25
|
+
end
|
26
|
+
|
27
|
+
LIST_SEP = ':'
|
28
|
+
def likely_list?(val)
|
29
|
+
# we have some work
|
30
|
+
# if the value includes our list separtor ":", we need to make sure whether a url:port combination is not a better fit
|
31
|
+
return false if !val.include?(LIST_SEP)
|
32
|
+
|
33
|
+
sep_cnt = val.count(LIST_SEP)
|
34
|
+
return true if sep_cnt > 2
|
35
|
+
|
36
|
+
# match scheme://url
|
37
|
+
return false if val =~ /^\w+\:\/\//
|
38
|
+
|
39
|
+
return true if sep_cnt == 2 # everything else with 2 separators is a list
|
40
|
+
|
41
|
+
# match display type strings address:digit.digit
|
42
|
+
return false if val =~ /\:\d.\d$/
|
43
|
+
|
44
|
+
# match something:number to be interpreted as addr:port
|
45
|
+
!(val =~ /.*\:\d+$/)
|
46
|
+
end
|
47
|
+
def list_to_ar(list)
|
48
|
+
list.split(LIST_SEP)
|
49
|
+
end
|
50
|
+
def ar_to_list(ar)
|
51
|
+
ar.join(LIST_SEP)
|
52
|
+
end
|
53
|
+
|
54
|
+
def cmd_set_env_var(name, value)
|
55
|
+
escaped = value.to_s.inspect.gsub("'"){ "\\'" }
|
56
|
+
"export #{name}=#{escaped}"
|
57
|
+
end
|
58
|
+
def cmd_unset_env_var(name)
|
59
|
+
"unset -v #{name}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Envo
|
2
|
+
module Shell
|
3
|
+
module WinCmd
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def installer
|
7
|
+
Cli::InstallerWinCmd
|
8
|
+
end
|
9
|
+
|
10
|
+
def path_var_name
|
11
|
+
'Path'
|
12
|
+
end
|
13
|
+
def home_var_name
|
14
|
+
'HOME'
|
15
|
+
end
|
16
|
+
|
17
|
+
def likely_abs_path?(val)
|
18
|
+
val =~ /^[a-zA-Z]\:\\/
|
19
|
+
end
|
20
|
+
def likely_rel_path?(val)
|
21
|
+
return !val.empty? && val[0] == '.'
|
22
|
+
end
|
23
|
+
def fix_path(path)
|
24
|
+
path.gsub('/', '\\')
|
25
|
+
end
|
26
|
+
|
27
|
+
LIST_SEP = ';'
|
28
|
+
def likely_list?(val)
|
29
|
+
val.include?(LIST_SEP)
|
30
|
+
end
|
31
|
+
def list_to_ar(list)
|
32
|
+
list.split(LIST_SEP)
|
33
|
+
end
|
34
|
+
def ar_to_list(ar)
|
35
|
+
ar.join(LIST_SEP)
|
36
|
+
end
|
37
|
+
|
38
|
+
def cmd_set_env_var(name, value)
|
39
|
+
escaped = value # TODO
|
40
|
+
"set #{name}=#{escaped}"
|
41
|
+
end
|
42
|
+
def cmd_unset_env_var(name)
|
43
|
+
"set #{name}="
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/envo/state.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module Envo
|
2
|
+
class State
|
3
|
+
def initialize(env)
|
4
|
+
@real_env = env.to_h
|
5
|
+
@work_env = nil
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :real_env
|
9
|
+
|
10
|
+
def set(name, val)
|
11
|
+
if val == nil
|
12
|
+
unset(name)
|
13
|
+
else
|
14
|
+
work_env[name] = val.to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def unset(name)
|
19
|
+
work_env.delete(name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def get(name)
|
23
|
+
work_env[name]
|
24
|
+
end
|
25
|
+
|
26
|
+
def work_env
|
27
|
+
return @work_env if @work_env
|
28
|
+
# if @real_env is ENV, we can use to_h to clone it into the work env
|
29
|
+
# however it can be an actual hash in which case to_h will return the same one
|
30
|
+
# and we would have to use clone
|
31
|
+
# so to make this work in all cases we preform a manual shallow copy
|
32
|
+
@work_env = @real_env.map { |k, v| [k, v] }.to_h
|
33
|
+
end
|
34
|
+
|
35
|
+
class Patch
|
36
|
+
def initialize(removed = [], changed = {}, added = {})
|
37
|
+
@removed = removed
|
38
|
+
@changed = changed
|
39
|
+
@added = added
|
40
|
+
end
|
41
|
+
|
42
|
+
def empty?
|
43
|
+
@removed.empty? && @changed.empty? && @added.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
attr_reader :removed, :changed, :added
|
47
|
+
end
|
48
|
+
|
49
|
+
def diff
|
50
|
+
return Patch.new if !@work_env
|
51
|
+
|
52
|
+
real_names = @real_env.keys
|
53
|
+
work_names = @work_env.keys
|
54
|
+
|
55
|
+
removed_names = real_names - work_names
|
56
|
+
added_names = work_names - real_names
|
57
|
+
preserved_names = real_names - removed_names
|
58
|
+
|
59
|
+
changed = preserved_names.map { |v|
|
60
|
+
r = @real_env[v]
|
61
|
+
w = @work_env[v]
|
62
|
+
|
63
|
+
r == w ? nil : [v, w]
|
64
|
+
}.compact.to_h
|
65
|
+
|
66
|
+
added = added_names.map { |v| [v, @work_env[v]] }.to_h
|
67
|
+
|
68
|
+
Patch.new(removed_names, changed, added)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Envo
|
2
|
+
class ListVal
|
3
|
+
def initialize(ar)
|
4
|
+
@ar = ar
|
5
|
+
end
|
6
|
+
attr_reader :ar
|
7
|
+
|
8
|
+
def insert(elem, pos = nil)
|
9
|
+
# assume unique elements
|
10
|
+
old_index = @ar.index(elem)
|
11
|
+
new_index = case pos
|
12
|
+
when :front then 0
|
13
|
+
when :back then -1
|
14
|
+
else old_index
|
15
|
+
end
|
16
|
+
|
17
|
+
return @ar << elem if !new_index
|
18
|
+
return @ar if new_index == old_index
|
19
|
+
return @ar.insert(new_index, elem) if !old_index
|
20
|
+
|
21
|
+
# we need to reorder
|
22
|
+
@ar.delete_at(old_index)
|
23
|
+
@ar.insert(new_index, elem)
|
24
|
+
end
|
25
|
+
def delete(elem)
|
26
|
+
@ar.delete(elem)
|
27
|
+
end
|
28
|
+
def delete_at(index)
|
29
|
+
@ar.delete_at(index)
|
30
|
+
end
|
31
|
+
def uniq!
|
32
|
+
@ar.uniq!
|
33
|
+
end
|
34
|
+
def clean!
|
35
|
+
uniq!
|
36
|
+
end
|
37
|
+
def shift(elem, dir)
|
38
|
+
i = @ar.index(elem)
|
39
|
+
return nil if i == nil
|
40
|
+
shift_at(i, dir)
|
41
|
+
end
|
42
|
+
def shift_at(i, dir)
|
43
|
+
return nil if i>@ar.size
|
44
|
+
|
45
|
+
if dir == :front
|
46
|
+
return i if i == 0
|
47
|
+
elem = ar[i]
|
48
|
+
@ar.delete_at i
|
49
|
+
@ar.unshift(elem)
|
50
|
+
0
|
51
|
+
elsif dir == :back
|
52
|
+
return i if i == (@ar.size-1)
|
53
|
+
elem = ar[i]
|
54
|
+
@ar.delete_at i
|
55
|
+
@ar << elem
|
56
|
+
@ar.size-1
|
57
|
+
elsif dir == :up
|
58
|
+
return i if i == 0
|
59
|
+
@ar[i-1], @ar[i] = @ar[i], @ar[i-1]
|
60
|
+
i - 1
|
61
|
+
elsif dir == :down
|
62
|
+
return i if i == (@ar.size-1)
|
63
|
+
@ar[i+1], @ar[i] = @ar[i], @ar[i+1]
|
64
|
+
i + 1
|
65
|
+
else
|
66
|
+
-1
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def pp_attribs(elem)
|
71
|
+
@ar.count(elem) > 1 ? 'D' : ' '
|
72
|
+
end
|
73
|
+
def pretty_print(ctx)
|
74
|
+
ctx.puts "["
|
75
|
+
@ar.each_with_index do |v, i|
|
76
|
+
str = pp_attribs(v) + ' '
|
77
|
+
str += "#{i}:".ljust(4)
|
78
|
+
str += v
|
79
|
+
ctx.puts str
|
80
|
+
end
|
81
|
+
ctx.puts ']'
|
82
|
+
end
|
83
|
+
|
84
|
+
# casts
|
85
|
+
def type
|
86
|
+
:list
|
87
|
+
end
|
88
|
+
def accept_assign?(other)
|
89
|
+
other.list?
|
90
|
+
end
|
91
|
+
def invalid_description
|
92
|
+
@ar.empty? ? "empty list" : nil
|
93
|
+
end
|
94
|
+
def list?
|
95
|
+
true
|
96
|
+
end
|
97
|
+
def to_list
|
98
|
+
return self
|
99
|
+
end
|
100
|
+
def accept_item?(item)
|
101
|
+
true
|
102
|
+
end
|
103
|
+
def to_s
|
104
|
+
raise StandardError.new "list can't be converted to String"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|