Module: EnvBash

Defined in:
lib/envbash/load.rb,
lib/envbash/read.rb

Defined Under Namespace

Classes: ScriptExitedEarly

Constant Summary collapse

FIXUPS =
%w{_ OLDPWD PWD SHLVL}

Class Method Summary collapse

Class Method Details

.load(envbash, into: ENV, override: false, remove: false, **kwargs) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
# File 'lib/envbash/load.rb', line 5

def EnvBash.load(envbash, into: ENV, override: false, remove: false, **kwargs)
  loaded = read(envbash, **kwargs)
  is_env = into.equal? ENV
  into = into.to_h if is_env
  if loaded
    into.select! {|k| loaded.include? k} if remove
    loaded.reject! {|k| into.include? k} unless override
    into.merge! loaded
  end
  ENV.replace into if is_env
end

.read(envbash, bash: 'bash', env: ENV, missing_ok: false, fixups: FIXUPS) ⇒ Object

Raises:



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/envbash/read.rb', line 12

def EnvBash.read(envbash, bash: 'bash', env: ENV, missing_ok: false, fixups: FIXUPS)
  # make sure the file exists and is readable.
  # alternatively we could test File.readable?(envbash) but this approach
  # raises Errno::ENOENT or Errno::EACCES which is what we want.
  begin
    File.open(envbash).close
  rescue Errno::ENOENT
    return if missing_ok
    raise
  end

  # construct an inline script which sources env.bash then prints the
  # resulting environment so it can be eval'd back into this process.
  inline = <<-EOT
      set -a
      source #{envbash.shellescape} >/dev/null
      #{Gem.ruby.shellescape} -e 'p ENV'
  EOT

  # Process.spawn treats env as overriding ENV, and anything that should be
  # omitted needs to have a nil value. If env == ENV then this is a noop.
  env = Hash[ENV.keys.map {|k| [k, nil]}].merge(env)

  # run the inline script with bash -c, capturing stdout. if there is any
  # error output from env.bash, it will pass through to stderr.
  # exit status is ignored.
  output, _ = Open3.capture2(env, 'bash', '-c', inline, :in=>"/dev/null")

  # the only stdout from the inline script should be
  # `p ENV` so there should be no syntax errors eval'ing this. however there
  # will be no output to eval if the sourced env.bash exited early, and that
  # indicates script failure.
  raise ScriptExitedEarly if output.empty?

  # the eval'd output should return a hash.
  nenv = eval(output)

  # there are a few environment variables that vary between this process and
  # running the inline script with bash -c, but are certainly not part of the
  # intentional settings in env.bash.
  for f in fixups
    if env[f]  # not .include? because env might have nil values
      nenv[f] = env[f]
    else
      nenv.delete(f)
    end
  end

  nenv
end