envo 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
+ SHA256:
3
+ metadata.gz: 54f5c8282f4d3219db6e7a5384316ed1f3fdd3e43d0e99fdfbb6d768436c8445
4
+ data.tar.gz: a9b2e63f76ae3dd5844694365f8dbdff259c3dcb485374583cbc258b3547a94e
5
+ SHA512:
6
+ metadata.gz: 8638df6a7acbd3bd61c7ea2d717308e3369091f96585367c6fcb2c7b3c88724efc9acb93a14d3e82f9b8808a6e82cb263957290a818da64c65b16d7e945f973b
7
+ data.tar.gz: 63e000afd0bf3f69394c02f221b92f910714e7e29e2f53fe2d0973e9fa201b552a514f3f68ca03f84c236db0f89ffb4fc7c8c107d5e709a129bd6f3297e9a037
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__) # For use/testing when no gem is installed
4
+ require 'envo'
5
+
6
+ if ARGV.size == 1 && ARGV[0] == '--version'
7
+ puts "installer for envo v#{Envo::VERSION} #{Envo::VERSION_TYPE}"
8
+ exit 0
9
+ end
10
+
11
+ begin
12
+ shell = Envo::HostShell
13
+ installer = shell.installer.new
14
+ exit installer.run(ARGV.dup)
15
+ rescue Envo::Error => e
16
+ STDERR.puts e.message
17
+ exit 1
18
+ end
@@ -0,0 +1,50 @@
1
+ # this is the proper method
2
+ # however we judge envo to be very low risk
3
+ # in terms of clashes, so just do the silly thing
4
+ # require 'securerandom'
5
+ # fname = SecureRandom.uuid
6
+
7
+ # silly thing
8
+ fname = "envo-" + Time.now.to_i.to_s(16) + '-' + rand(1_000_000).to_s(16) + ".bat"
9
+ # yes, simply always add the .bat extension. Linux doesn't care anyway
10
+
11
+ # the ultimate multi-platform temp dir finder
12
+ # again, the proper way would be to require 'rbconfig'
13
+ # find the os, and use OS-specific ways, but we're trying to be fast here
14
+
15
+ def temp_dirs(&block)
16
+ yield 'C:/windows/system32'
17
+ yield '/tmp' # most unixes
18
+ yield ENV['TMP'] # good guess for windows
19
+ yield '/var/tmp'
20
+ yield ENV['TEMP']
21
+ yield ENV['TEMPDIR']
22
+ yield Dir.home
23
+ yield __dir__ # why not
24
+ yield Dir.pwd # last resort
25
+ end
26
+
27
+ def existing_temp_dirs(&block)
28
+ temp_dirs do |dir|
29
+ yield dir if dir && File.exist?(dir)
30
+ end
31
+ end
32
+
33
+ existing_temp_dirs do |dir|
34
+ path = File.join(dir, fname)
35
+
36
+ begin
37
+ File.open(path, 'w') {}
38
+ # on windows fix path in order for 'del' to work
39
+ path.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
40
+ puts path
41
+ exit 0
42
+ rescue
43
+ # just try the next entry
44
+ end
45
+ end
46
+
47
+ STDERR.puts "ERROR: Could not create envo temp file"
48
+ # write something just so the rest can continue up to the point where this file needs to be written
49
+ puts 'error.bat'
50
+ exit 1
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if ARGV.size == 1 && ARGV[0] == 'g'
4
+ require_relative 'envo_gen_tmp_helper'
5
+ end
6
+
7
+ argv = ARGV.dup
8
+
9
+ if argv.size < 2 || argv.shift != 'pld'
10
+ STDERR.puts <<~ERR
11
+ envo_run needs to be called from envo
12
+ if you don't have the command 'envo', run 'envo-install'
13
+ ERR
14
+ exit 1
15
+ end
16
+
17
+ payload_path = argv.shift
18
+ payload = File.open(payload_path, 'w')
19
+
20
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__) # For use/testing when no gem is installed
21
+ require "envo"
22
+
23
+ begin
24
+ host = Envo::Host.new(Envo::HostShell)
25
+ r = Envo::Cli::Runner.new(host, payload)
26
+ exit r.run(argv)
27
+ rescue Envo::Error => e
28
+ STDERR.puts e.message
29
+ exit 1
30
+ end
@@ -0,0 +1,45 @@
1
+ $LOAD_PATH.unshift __dir__ # For use/testing when no gem is installed
2
+
3
+ module Envo
4
+ autoload :VERSION, "envo/version"
5
+ autoload :Error, "envo/error"
6
+ autoload :NoVal, "envo/val/no_val"
7
+ autoload :StringVal, "envo/val/string_val"
8
+ autoload :ListVal, "envo/val/list_val"
9
+ autoload :PathVal, "envo/val/path_val"
10
+ autoload :PathListVal, "envo/val/path_list_val"
11
+ autoload :CmdSet, "envo/cmd_set"
12
+ autoload :CmdReset, "envo/cmd_reset"
13
+ autoload :CmdUnset, "envo/cmd_unset"
14
+ autoload :CmdListAdd, "envo/cmd_list_add"
15
+ autoload :CmdListDel, "envo/cmd_list_del"
16
+ autoload :CmdShow, "envo/cmd_show"
17
+ autoload :CmdSwap, "envo/cmd_swap"
18
+ autoload :CmdRun, "envo/cmd_run"
19
+ autoload :CmdList, "envo/cmd_list"
20
+ autoload :CmdPath, "envo/cmd_path"
21
+ autoload :CmdClean, "envo/cmd_clean"
22
+ autoload :CmdCopy, "envo/cmd_copy"
23
+ autoload :State, "envo/state"
24
+ autoload :ValBuilder, "envo/val/val_builder"
25
+ autoload :Context, "envo/context"
26
+ autoload :ParsedCmd, "envo/parsed_cmd"
27
+ autoload :CliParser, "envo/cli_parser"
28
+ autoload :ScriptParser, "envo/script_parser"
29
+ autoload :HostShell, "envo/host_shell"
30
+ autoload :Host, "envo/host"
31
+ autoload :ParseResult, "envo/parse_result"
32
+ autoload :Logger, "envo/logger"
33
+
34
+ module Shell
35
+ autoload :Bash, 'envo/shell/bash'
36
+ autoload :WinCmd, 'envo/shell/win_cmd'
37
+ end
38
+
39
+ module Cli
40
+ autoload :Runner, 'envo/cli/runner'
41
+ autoload :Help, 'envo/cli/help'
42
+ autoload :InstallerWinCmd, 'envo/cli/installer_win_cmd'
43
+ autoload :InstallerBash, 'envo/cli/installer_bash'
44
+ end
45
+ end
@@ -0,0 +1,11 @@
1
+ @echo off
2
+ :: we can't use `setlocal` here, since we actually WANT to leak
3
+ :: after all, this script is supposed to set env vars
4
+ :: however in order to get the result of `run g` we need to assign it to
5
+ :: a local var... which we must leak :(
6
+ :: so... just choose the a very unlikely duplicate var name
7
+ for /f "delims=" %%A in ('envo_run g') do set _ENVO_PLD=%%A
8
+
9
+ envo_run pld "%_ENVO_PLD%" %*
10
+ call "%_ENVO_PLD%"
11
+ del "%_ENVO_PLD%"
@@ -0,0 +1,6 @@
1
+ envo() {
2
+ local tmpfile=$(envo_run g)
3
+ envo_run pld "${tmpfile}" "$@"
4
+ source "${tmpfile}"
5
+ rm "${tmpfile}"
6
+ }
@@ -0,0 +1,20 @@
1
+ module Envo
2
+ module Cli
3
+ class Help
4
+ def initialize
5
+ @commands = []
6
+ end
7
+ def add_cmd(usage, text)
8
+ @commands << [usage, text]
9
+ end
10
+ def print(stream)
11
+ @commands.each do |cmd|
12
+ stream.puts " * #{cmd[0]}"
13
+ cmd[1].each_line do |line|
14
+ stream.puts " #{line}"
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,131 @@
1
+ # require 'open3'
2
+
3
+ module Envo
4
+ module Cli
5
+ class InstallerBash
6
+ # def detect_installed_envo?()
7
+ # stdout, stderr, code = Open3.capture3("bash -ic 'command -v envo'")
8
+ # code.success?
9
+ # end
10
+
11
+ ENVO_INSTALLATION_BEGIN = '### BEGIN envo installation (don\'t remove line)'
12
+ ENVO_INSTALLATION_END = '### END envo installation (don\'t remove line)'
13
+
14
+ def find_existing_installation_data(dotfile)
15
+ return nil if !File.exist?(dotfile)
16
+
17
+ raise Envo::Error.new "'#{dotfile}' exists but is not a file. You need to choose a file." if !File.file?(dotfile)
18
+
19
+ lines = File.readlines(dotfile)
20
+ first = nil
21
+ last = nil
22
+ lines.each_with_index do |l, i|
23
+ lc = l.chomp
24
+ if lc == ENVO_INSTALLATION_BEGIN
25
+ first = i
26
+ elsif lc == ENVO_INSTALLATION_END
27
+ last = i
28
+ end
29
+ end
30
+
31
+ return nil if !first && !last
32
+
33
+ if !first || !last
34
+ raise Envo::Error.new <<~EOF
35
+ #{dotfile}' contains a broken confy insallation.
36
+ You need to remove it manually
37
+ EOF
38
+ end
39
+
40
+ num = last - first + 1
41
+ return {first: first, num: num, lines: lines}
42
+ end
43
+
44
+ SOURCE_FILE = 'envo.sh'
45
+ def try_install(dotfile)
46
+ install_lines = [
47
+ ENVO_INSTALLATION_BEGIN,
48
+ "### envo #{VERSION}",
49
+ File.read(File.join(__dir__, SOURCE_FILE)),
50
+ ENVO_INSTALLATION_END
51
+ ]
52
+
53
+ found = find_existing_installation_data(dotfile)
54
+
55
+ openmode = 'a'
56
+ if found
57
+ lines = found[:lines]
58
+ lines[found[:first], found[:num]] = install_lines
59
+ install_lines = lines
60
+ openmode = 'w'
61
+ end
62
+
63
+ File.open(dotfile, openmode) { |f| f.puts install_lines }
64
+ puts <<~EOF
65
+ Sucessfully installed confy to '#{dotfile}'
66
+ Source the file, or restart the bash session if the file is auto-sourced.
67
+ EOF
68
+ end
69
+
70
+ def try_uninstall(dotfile)
71
+ found = find_existing_installation_data(dotfile)
72
+ if !found || found[:num] == 0
73
+ raise Envo::Error.new "'#{dotfile}' doesn't seem to contain an envo installation"
74
+ end
75
+
76
+ lines = found[:lines]
77
+ lines[found[:first], found[:num]] = []
78
+ File.open(dotfile, 'w') { |f| f.puts lines }
79
+ puts "Suncessfully uninstalled confy from '#{dotfile}'"
80
+ end
81
+
82
+ DEFAULT_DOTFILE = File.join(Dir.home, '.bashrc')
83
+ USAGE = <<~EOF
84
+ usage: envo-install [u] [--dotfile <path>]
85
+ EOF
86
+ def run(argv)
87
+ if argv.empty?
88
+ try_install(DEFAULT_DOTFILE)
89
+ return 0
90
+ end
91
+
92
+ if argv[0] == '--help' || argv[0] == '-?'
93
+ puts "installer for envo v#{Envo::VERSION} #{Envo::VERSION_TYPE}"
94
+ puts USAGE
95
+ puts
96
+ puts ' u - uninstall envo'
97
+ puts ' --dotfile <file> - install to or uninstall form a specified dotfile'
98
+ return 0
99
+ end
100
+
101
+ if argv[0] == 'u'
102
+ @uninstalling = true
103
+ argv.shift
104
+ end
105
+
106
+ dotfile = DEFAULT_DOTFILE
107
+ if !argv.empty?
108
+ arg = argv.shift
109
+ if arg != '--dotfile'
110
+ STDERR.puts "Unknown argument #{arg}"
111
+ STDERR.puts USAGE
112
+ return 1
113
+ end
114
+ if argv.empty?
115
+ STDERR.puts "Missing dotfile path"
116
+ STDERR.puts USAGE
117
+ return 1
118
+ end
119
+ dotfile = argv.shift
120
+ end
121
+
122
+ # if detect_installed_envo?
123
+ # puts "It seems that you already have envo installed"
124
+ # puts "Do you want to reinstall it? (y/n)"
125
+ # end
126
+ @uninstalling ? try_uninstall(dotfile) : try_install(dotfile)
127
+ return 0
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,96 @@
1
+ module Envo
2
+ module Cli
3
+ class InstallerWinCmd
4
+ def each_path_dir
5
+ ENV["Path"].split(';').each { |p| yield p }
6
+ end
7
+
8
+ ENVO_RUN_CMD = "envo_run"
9
+ INSTALL_FILE = "envo.bat"
10
+ SOURCE_FILE = "envo.bat"
11
+
12
+ def try_install(path)
13
+ if !path
14
+ each_path_dir do |dir|
15
+ if File.file?(File.join(dir, ENVO_RUN_CMD))
16
+ path = dir
17
+ break
18
+ end
19
+ end
20
+ raise Error.new("Couldn't find a good place to install envo. Please use '--path <path>' to provide one") if !path
21
+ end
22
+ raise Error.new("'#{path}' is not an existing directory") if !File.directory?(path)
23
+
24
+ src = File.read(File.join(__dir__, SOURCE_FILE))
25
+ target = File.join(path, INSTALL_FILE)
26
+ File.open(target, 'w') do |f|
27
+ f.puts ":: envo #{VERSION}"
28
+ f.write(src)
29
+ end
30
+ puts "Successfully installed #{target}"
31
+ end
32
+
33
+ def try_uninstall(path)
34
+ if path
35
+ file = File.join(path, INSTALL_FILE)
36
+ raise Error.new "Couldn't find an existing envo installation in #{path}" if !File.file?(file)
37
+ File.delete(file)
38
+ puts "Sucessfully uninstalled #{file}"
39
+ return
40
+ else
41
+ each_path_dir do |dir|
42
+ file = File.join(dir, INSTALL_FILE)
43
+ next if !File.file?(file)
44
+ File.delete(file)
45
+ puts "Sucessfully uninstalled #{file}"
46
+ return
47
+ end
48
+ end
49
+ raise Error.new "Couldn't find an existing envo installation to uninstall"
50
+ end
51
+
52
+ USAGE = <<~EOF
53
+ usage: envo-install [u] [--path <path>]
54
+ EOF
55
+ def run(argv)
56
+ if argv.empty?
57
+ try_install(nil)
58
+ return 0
59
+ end
60
+
61
+ if argv[0] == '--help' || argv[0] == '-?'
62
+ puts "installer for envo v#{Envo::VERSION} #{Envo::VERSION_TYPE}"
63
+ puts USAGE
64
+ puts
65
+ puts ' u - uninstall envo'
66
+ puts ' --path <path> - install to or uninstall form a specified directory'
67
+ return 0
68
+ end
69
+
70
+ if argv[0] == 'u'
71
+ @uninstalling = true
72
+ argv.shift
73
+ end
74
+
75
+ path = nil
76
+ if !argv.empty?
77
+ arg = argv.shift
78
+ if arg != '--path'
79
+ STDERR.puts "Unknown argument #{arg}"
80
+ STDERR.puts USAGE
81
+ return 1
82
+ end
83
+ if argv.empty?
84
+ STDERR.puts "Missing path"
85
+ STDERR.puts USAGE
86
+ return 1
87
+ end
88
+ path = argv.shift
89
+ end
90
+
91
+ @uninstalling ? try_uninstall(path) : try_install(path)
92
+ return 0
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,129 @@
1
+ module Envo
2
+ module Cli
3
+ class Runner
4
+ def initialize(host, payload)
5
+ @host = host
6
+ @payload = payload
7
+ end
8
+ attr_reader :host, :payload
9
+
10
+ VERSION_TEXT = "envo v#{Envo::VERSION} #{Envo::VERSION_TYPE}"
11
+ USAGE = <<~EOF
12
+ usage: envo [--version] [--help] <command> [<args>]
13
+ EOF
14
+
15
+ module Opts
16
+ extend self
17
+ def parse_cli(opt)
18
+ case opt
19
+ when '--force', '-f' then return {interact: :force}
20
+ when '--no-force', '-nf' then return {interact: :noforce}
21
+ when '--interactive' then return {interact: :interact}
22
+ when '--raw', '-r' then return {raw: true}
23
+ else raise Envo::Error.new "unknown command line option: #{opt}"
24
+ end
25
+ end
26
+ def print_arg(log, arg, text)
27
+ log.puts " #{arg.ljust(15)} - #{text}"
28
+ end
29
+ def print_help(log)
30
+ print_arg log, '--force, -f', 'force the execution of the command disregarding checks'
31
+ print_arg log, '--interactive', '(default) ask a question when a check fails'
32
+ print_arg log, '--no-force, -nf', 'produce an error instead of asking questions for checks'
33
+ print_arg log, '--raw, -r', 'don\'t infer types and treat each value as a raw string'
34
+ end
35
+ Defaults = {
36
+ interact: :interact,
37
+ log_level: Logger::INFO
38
+ }
39
+ end
40
+
41
+ Commands = [
42
+ CmdShow,
43
+ CmdSet,
44
+ CmdReset,
45
+ CmdUnset,
46
+ CmdList,
47
+ CmdListAdd,
48
+ CmdListDel,
49
+ CmdClean,
50
+ CmdCopy,
51
+ CmdSwap,
52
+ CmdPath,
53
+ CmdRun,
54
+ ]
55
+
56
+ def print_version
57
+ @log.puts VERSION_TEXT
58
+ end
59
+
60
+ def print_help
61
+ print_version
62
+ @log.puts USAGE
63
+ @log.puts ''
64
+ @log.puts 'Commands:'
65
+ help = Help.new
66
+ Commands.each do |cmd|
67
+ cmd.register_help(help)
68
+ end
69
+ help.print(@log)
70
+ @log.puts ''
71
+ @log.puts 'Common options:'
72
+ Opts.print_help(@log)
73
+ end
74
+
75
+ def check_help_ver(argv)
76
+ raise Error.new USAGE if argv.empty?
77
+ case argv[0]
78
+ when '--help', '-h', '-?'
79
+ print_help
80
+ true
81
+ when '--version'
82
+ print_version
83
+ true
84
+ else
85
+ false
86
+ end
87
+ end
88
+
89
+ def do_run(argv)
90
+ return if check_help_ver(argv)
91
+
92
+ parser = CliParser.new(Opts)
93
+ Commands.each { |cmd| cmd.register_cli_parser(parser) }
94
+ parsed = parser.parse(argv)
95
+
96
+ ctx = Context.new(@host, @log, Opts::Defaults)
97
+ ctx.execute(parsed)
98
+
99
+ patch = ctx.state.diff
100
+
101
+ return if patch.empty?
102
+
103
+ patch.removed.each { |name|
104
+ @payload.puts @host.shell.cmd_unset_env_var(name)
105
+ puts @host.shell.cmd_unset_env_var(name)
106
+ }
107
+ patch.changed.each { |name, val|
108
+ @payload.puts @host.shell.cmd_set_env_var(name, val)
109
+ puts @host.shell.cmd_set_env_var(name, val)
110
+ }
111
+ patch.added.each { |name, val|
112
+ @payload.puts @host.shell.cmd_set_env_var(name, val)
113
+ puts @host.shell.cmd_set_env_var(name, val)
114
+ }
115
+ end
116
+
117
+ def run(argv)
118
+ @log = Logger.new
119
+ begin
120
+ do_run(argv)
121
+ return 0
122
+ rescue Error => e
123
+ @log.error e.message
124
+ return 1
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end