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/cmd_run.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
module Envo
|
2
|
+
class CmdRun
|
3
|
+
Name = 'run'
|
4
|
+
def self.register_help(help)
|
5
|
+
help.add_cmd 'run <script>', <<~EOF
|
6
|
+
run a script of envo commands
|
7
|
+
if script is a relative or an absolute path, it tries to load the exact filename
|
8
|
+
otherwise it searches for '<script>.envoscript' in .envo/ subdirs of current tree and in <home>/.envo/
|
9
|
+
EOF
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.register_cli_parser(parser)
|
13
|
+
parser.add_cmd(Name, ->(cmd, args) { parse_cli(args) })
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.register_script_parser(parser)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.parse_cli(args)
|
20
|
+
opts = CliParser.filter_opts(args)
|
21
|
+
raise Envo::Error.new "run: provide a single script name. Use 'run <script>'" if args.size != 1
|
22
|
+
ParsedCmd.new(CmdRun.new(args[0]), opts)
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(script)
|
26
|
+
@script = script
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :script
|
30
|
+
|
31
|
+
module Opts
|
32
|
+
extend self
|
33
|
+
def parse_script(opt)
|
34
|
+
case opt
|
35
|
+
when 'force' then return {interact: :force}
|
36
|
+
when 'no-force' then return {interact: :noforce}
|
37
|
+
when 'interactive' then return {interact: :interact}
|
38
|
+
when 'raw' then return {raw: true}
|
39
|
+
else raise Envo::Error.new "script option: #{opt}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def execute(ctx)
|
45
|
+
file = ctx.find_script(@script)
|
46
|
+
lines = ctx.load_script(file)
|
47
|
+
parser = ScriptParser.new(Opts)
|
48
|
+
|
49
|
+
[
|
50
|
+
CmdShow,
|
51
|
+
CmdSet,
|
52
|
+
CmdReset,
|
53
|
+
CmdUnset,
|
54
|
+
CmdList,
|
55
|
+
CmdClean,
|
56
|
+
CmdCopy,
|
57
|
+
CmdSwap,
|
58
|
+
CmdPath,
|
59
|
+
CmdRun,
|
60
|
+
].each { |cmd| cmd.register_script_parser(parser) }
|
61
|
+
|
62
|
+
res = parser.parse(lines)
|
63
|
+
|
64
|
+
scope = ctx.new_scope({interact: :force})
|
65
|
+
scope.execute(res)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/envo/cmd_set.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
module Envo
|
2
|
+
class CmdSet
|
3
|
+
Name = 'set'
|
4
|
+
def self.register_help(help)
|
5
|
+
help.add_cmd "set <name>=<val>", <<~EOF
|
6
|
+
set a value to an environment variable
|
7
|
+
'set name=' unsets the value
|
8
|
+
EOF
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.register_cli_parser(parser)
|
12
|
+
parser.add_cmd(Name, ->(cmd, args) { parse_cli(args) })
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.register_script_parser(parser)
|
16
|
+
parser.add_cmd(Name, ->(cmd, tokens, opts) { parse_tokens(tokens, opts) })
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.parse_cli(args)
|
20
|
+
opts = CliParser.filter_opts(args)
|
21
|
+
parse_tokens(args, opts)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.parse_tokens(args, opts)
|
25
|
+
# find first instance of equals
|
26
|
+
i = args.find_index { |arg| arg =~ /=/ }
|
27
|
+
raise Envo::Error.new "set: missing '='. Use 'set <name> = <val>'" if !i
|
28
|
+
|
29
|
+
elem = args[i]
|
30
|
+
eq_i = elem.index('=')
|
31
|
+
|
32
|
+
first = elem[0...eq_i]
|
33
|
+
second = elem[eq_i+1..]
|
34
|
+
split = [first, '=', second].select { |s| !s.empty? }
|
35
|
+
|
36
|
+
args[i..i] = split
|
37
|
+
|
38
|
+
i = args.index('=')
|
39
|
+
raise Envo::Error.new "set: bad name '#{args[0...i].join(' ')}'. Use 'set <name> = <val>'" if i != 1
|
40
|
+
|
41
|
+
return ParsedCmd.new(CmdUnset.new([args[0]]), opts) if args.size == 2
|
42
|
+
|
43
|
+
ParsedCmd.new(CmdSet.new(args[0], args[2..]), opts)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.parse_script(args)
|
47
|
+
puts "#{Name} parse_script"
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize(name, value)
|
51
|
+
@name = name
|
52
|
+
@value = value
|
53
|
+
end
|
54
|
+
|
55
|
+
attr_accessor :name, :value
|
56
|
+
|
57
|
+
def execute(ctx)
|
58
|
+
ename = ctx.expand_name(@name)
|
59
|
+
new_val = ctx.expand_value(@value)
|
60
|
+
|
61
|
+
old_val = ctx.smart_get(ename)
|
62
|
+
|
63
|
+
ok = old_val.type == new_val.type
|
64
|
+
ok ||= old_val.accept_assign?(new_val)
|
65
|
+
ok ||= ctx.ask("Assign #{new_val.type} to #{old_val.type}?")
|
66
|
+
raise Envo::Error.new "set: assignment of #{new_val.type} to #{old_val.type}" if !ok
|
67
|
+
|
68
|
+
idesc = new_val.invalid_description
|
69
|
+
ok = !idesc
|
70
|
+
ok ||= ctx.ask("Assign #{idesc} to #{ename}?")
|
71
|
+
raise Envo::Error.new "set: assignment of #{idesc} to #{ename}" if !ok
|
72
|
+
|
73
|
+
ctx.smart_set(ename, new_val)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Envo
|
2
|
+
class CmdShow
|
3
|
+
Name = 'show'
|
4
|
+
def self.register_help(help)
|
5
|
+
help.add_cmd "show <name> ...", <<~EOF
|
6
|
+
show values of environment variables
|
7
|
+
--name - display the name of the variable along with the value
|
8
|
+
shorhand: 's'
|
9
|
+
EOF
|
10
|
+
|
11
|
+
help.add_cmd 'rshow <name> ...', <<~EOF
|
12
|
+
show the *raw* value of environment variables with no pretty prints
|
13
|
+
a shortcut to 'show --raw <name> ...'
|
14
|
+
EOF
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.register_cli_parser(parser)
|
18
|
+
parser.add_cmd('show', ->(cmd, args) { parse_cli(args) })
|
19
|
+
parser.add_cmd('s', ->(cmd, args) { parse_cli(args) })
|
20
|
+
parser.add_cmd('rshow', ->(cmd, args) { parse_cli(args + ['--raw']) })
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.register_script_parser(parser)
|
24
|
+
parser.add_cmd(Name, ->(cmd, tokens, opts) { parse_script(tokens, opts) })
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.parse_cli(args)
|
28
|
+
opts = CliParser.filter_opts(args)
|
29
|
+
show_names = false
|
30
|
+
opts.filter! do |opt|
|
31
|
+
if opt == '--name'
|
32
|
+
show_names = true
|
33
|
+
false
|
34
|
+
else
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
ParsedCmd.new(CmdShow.new(args, show_names), opts)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.parse_script(tokens, opts)
|
42
|
+
show_names = false
|
43
|
+
opts.filter! do |opt|
|
44
|
+
if opt == 'name'
|
45
|
+
show_names = true
|
46
|
+
false
|
47
|
+
else
|
48
|
+
true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
ParsedCmd.new(CmdShow.new(tokens, show_names), opts)
|
52
|
+
end
|
53
|
+
|
54
|
+
def initialize(names, show_names)
|
55
|
+
raise Error.new 'show: no names provided' if names.empty?
|
56
|
+
@names = names
|
57
|
+
@show_names = show_names
|
58
|
+
end
|
59
|
+
|
60
|
+
attr_reader :names, :show_names
|
61
|
+
|
62
|
+
def execute(ctx)
|
63
|
+
@names.each do |name|
|
64
|
+
ename = ctx.expand_name(name)
|
65
|
+
|
66
|
+
pname = show_names ? "#{ename}=" : ''
|
67
|
+
|
68
|
+
if ctx.raw?
|
69
|
+
ctx.puts("#{pname}#{ctx.raw_get(ename)}")
|
70
|
+
else
|
71
|
+
val = ctx.smart_get(ename)
|
72
|
+
if val.type == :empty
|
73
|
+
ctx.puts("No var with name #{ename}")
|
74
|
+
else
|
75
|
+
ctx.print(pname)
|
76
|
+
val.pretty_print(ctx)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Envo
|
2
|
+
class CmdSwap
|
3
|
+
Name = 'swap'
|
4
|
+
def self.register_help(help)
|
5
|
+
help.add_cmd 'swap <name1> <name2>', "swap values of two environment variables"
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.register_cli_parser(parser)
|
9
|
+
parser.add_cmd(Name, ->(cmd, args) { parse_cli(args) })
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.register_script_parser(parser)
|
13
|
+
parser.add_cmd(Name, ->(cmd, tokens, opts) { parse_tokens(tokens, opts) })
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.parse_cli(args)
|
17
|
+
opts = CliParser.filter_opts(args)
|
18
|
+
parse_tokens(args, opts)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.parse_tokens(args, opts)
|
22
|
+
raise Envo::Error.new "swap: provide two names to swap. Use 'swap <name1> <name2>'" if args.size != 2
|
23
|
+
ParsedCmd.new(CmdSwap.new(args[0], args[1]), opts)
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(name_a, name_b)
|
27
|
+
@name_a = name_a
|
28
|
+
@name_b = name_b
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_accessor :name_a, :name_b
|
32
|
+
|
33
|
+
def execute(ctx)
|
34
|
+
ea = ctx.expand_name(@name_a)
|
35
|
+
raw_a = ctx.raw_get(ea)
|
36
|
+
raise Envo::Error.new "swap: no such var '#{ea}'" if !raw_a && !ctx.force?
|
37
|
+
|
38
|
+
eb = ctx.expand_name(@name_b)
|
39
|
+
raw_b = ctx.raw_get(eb)
|
40
|
+
raise Envo::Error.new "swap: no such var '#{eb}'" if !raw_b && !ctx.force?
|
41
|
+
|
42
|
+
return if ea == eb # swap something with itself...
|
43
|
+
return if !raw_a && !raw_b # no point in doing anything if they don't exist
|
44
|
+
|
45
|
+
ctx.raw_set(ea, raw_b)
|
46
|
+
ctx.raw_set(eb, raw_a)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Envo
|
2
|
+
class CmdUnset
|
3
|
+
Name = 'unset'
|
4
|
+
def self.register_help(help)
|
5
|
+
help.add_cmd 'unset <name> ...', "unset values of one or more environment variables"
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.register_cli_parser(parser)
|
9
|
+
parser.add_cmd(Name, ->(cmd, args) { parse_cli(args) })
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.register_script_parser(parser)
|
13
|
+
parser.add_cmd(Name, ->(cmd, tokens, opts) { parse_tokens(tokens, opts) })
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.parse_cli(args)
|
17
|
+
opts = CliParser.filter_opts(args)
|
18
|
+
ParsedCmd.new(CmdUnset.new(args), opts)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.parse_tokens(tokens, opts)
|
22
|
+
ParsedCmd.new(CmdUnset.new(tokens), opts)
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(names)
|
26
|
+
raise Envo::Error.new 'unset: no names provided' if names.empty?
|
27
|
+
@names = names
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :names
|
31
|
+
|
32
|
+
def execute(ctx)
|
33
|
+
@names.each do |name|
|
34
|
+
ename = ctx.expand_name(name)
|
35
|
+
raw_old_val = ctx.raw_get(ename)
|
36
|
+
|
37
|
+
if raw_old_val
|
38
|
+
ctx.unset(ename)
|
39
|
+
else
|
40
|
+
raise Envo::Error.new "unset: no such var '#{ename}'" if !ctx.force?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/envo/context.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
module Envo
|
2
|
+
class Context
|
3
|
+
def initialize(host, log, opts, state = nil)
|
4
|
+
@host = host
|
5
|
+
@log = log
|
6
|
+
@default_opts = opts
|
7
|
+
@opts = opts
|
8
|
+
reflect_opts_change
|
9
|
+
|
10
|
+
@state = state || State.new(host.env)
|
11
|
+
|
12
|
+
create_common_locals
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :host, :state
|
16
|
+
|
17
|
+
# create another context based on this one (same state, log, and host)
|
18
|
+
# which provides different opts an locals
|
19
|
+
# thus scripts can be executed which don't leak values in the scope above
|
20
|
+
def new_scope(defaults = {})
|
21
|
+
Context.new(@host, @log, @opts.merge(defaults), @state)
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_script(script)
|
25
|
+
if @host.shell.likely_rel_path?(script) || @host.shell.likely_abs_path?(script)
|
26
|
+
return script if @host.path_exists?(script)
|
27
|
+
raise Envo::Error.new "'#{script}' doesn't exist"
|
28
|
+
end
|
29
|
+
|
30
|
+
# look for '.envo/<file>.envoscript'
|
31
|
+
script = script + '.envoscript'
|
32
|
+
dir = @host.pwd
|
33
|
+
found = while true
|
34
|
+
check = File.join(dir, '.envo', script)
|
35
|
+
break check if @host.path_exists?(check)
|
36
|
+
new_dir = File.dirname(dir)
|
37
|
+
break nil if new_dir == dir
|
38
|
+
dir = new_dir
|
39
|
+
end
|
40
|
+
|
41
|
+
if !found
|
42
|
+
check = File.join(@host.home, '.envo', script)
|
43
|
+
found = check if @host.path_exists?(check)
|
44
|
+
end
|
45
|
+
|
46
|
+
raise Envo::Error.new "Can't find '#{script}' in .envo/ parent dirs or in <home>/.envo/" if !found
|
47
|
+
found
|
48
|
+
end
|
49
|
+
|
50
|
+
def load_script(path)
|
51
|
+
File.readlines(path)
|
52
|
+
end
|
53
|
+
|
54
|
+
# parse access
|
55
|
+
def expand_name(name)
|
56
|
+
@locals[name] || name
|
57
|
+
end
|
58
|
+
def expand_value(val)
|
59
|
+
if raw?
|
60
|
+
if val.class == Array
|
61
|
+
if val.size == 1
|
62
|
+
StringVal.new(val[0])
|
63
|
+
else
|
64
|
+
ListVal.new(val)
|
65
|
+
end
|
66
|
+
else
|
67
|
+
StringVal.new(val)
|
68
|
+
end
|
69
|
+
else
|
70
|
+
ValBuilder.from_user_text(val, @host)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
# local vars
|
74
|
+
def local_var_name?(name)
|
75
|
+
name =~ /^@[a-zA-Z]/
|
76
|
+
end
|
77
|
+
def set_local_var(name, value)
|
78
|
+
@locals[name] = value
|
79
|
+
end
|
80
|
+
def create_common_locals
|
81
|
+
@locals = {
|
82
|
+
'@path' => host.shell.path_var_name,
|
83
|
+
'@home' => host.shell.home_var_name,
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
# env access
|
88
|
+
def smart_get(name)
|
89
|
+
ValBuilder.from_env_string(raw_get(name), @host)
|
90
|
+
end
|
91
|
+
def raw_get(name)
|
92
|
+
@state.get(name)
|
93
|
+
end
|
94
|
+
|
95
|
+
def smart_set(name, value)
|
96
|
+
if value.list?
|
97
|
+
rv = @host.shell.ar_to_list(value.ar)
|
98
|
+
raw_set(name, rv)
|
99
|
+
else
|
100
|
+
raw_set(name, value.to_env_s)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
def raw_set(name, value)
|
104
|
+
@state.set(name, value)
|
105
|
+
end
|
106
|
+
|
107
|
+
def unset(name)
|
108
|
+
@state.unset(name)
|
109
|
+
end
|
110
|
+
|
111
|
+
# opt queries
|
112
|
+
def raw?
|
113
|
+
@opts[:raw]
|
114
|
+
end
|
115
|
+
def force?
|
116
|
+
@opts[:interact] == :force
|
117
|
+
end
|
118
|
+
def noforce?
|
119
|
+
@opts[:interact] == :noforce
|
120
|
+
end
|
121
|
+
def interact?
|
122
|
+
@opts[:interact] == :interact
|
123
|
+
end
|
124
|
+
|
125
|
+
def reflect_opts_change
|
126
|
+
@log.max_level = @opts[:log_level]
|
127
|
+
end
|
128
|
+
|
129
|
+
# io
|
130
|
+
def ask(question)
|
131
|
+
return true if force?
|
132
|
+
return false if noforce?
|
133
|
+
|
134
|
+
print "#{question} (y/n): "
|
135
|
+
answer = STDIN.gets.chomp
|
136
|
+
answer.downcase!
|
137
|
+
return answer == 'y' || answer == 'yes'
|
138
|
+
end
|
139
|
+
|
140
|
+
def error(text); @log.error(text); end
|
141
|
+
def warn(text); @log.warn(text); end
|
142
|
+
def print(text); @log.print(text); end
|
143
|
+
def puts(text); @log.puts(text); end
|
144
|
+
def debug(text); @log.debug(text); end
|
145
|
+
|
146
|
+
# execution
|
147
|
+
def execute(pack)
|
148
|
+
pack_opts = pack.opts
|
149
|
+
pack.cmds.each do |cmd|
|
150
|
+
@opts = @default_opts.merge(cmd.opts, pack_opts)
|
151
|
+
reflect_opts_change
|
152
|
+
cmd.cmd.execute(self)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|