Braintree和.env文件的使用

206 阅读2分钟

我们在Braintree的许多新应用程序使用环境变量进行配置。这符合Twelve-Factor原则,并使在多个环境中运行同一工件更加容易。在我们的案例中,工件是一个Docker镜像。Docker原生支持env文件,所以我们可以将配置写入文件,然后使用docker run --env-file .env ...

在本地开发中,我们使用.env 文件进行配置,一般使用docker-compose,它也支持这个功能。

# docker-compose.yml

myapp:
  image: myapp
  ports:
    - "8080:8080" 
  env_file: .env

然而,我们的构建工具Bazel并不支持这个功能。相反,你可以在命令行中传递单个环境变量。

bazel test --test_env EMAIL=a@example.com --test_env MODE=dev //...

我在GitHub上开了一个关于这个功能的问题(github.com/bazelbuild/…

使用方法

我们决定把我们常用的bazel调用包装成一个脚本,知道如何处理我们的.env 文件。

下面是它的用法。

./bazel build
./bazel build //myapp

./bazel test
./bazel test //myapp:test

./bazel run //myapp

构建和测试命令允许目标,如果没有通过,则假定你想要的一切(//...)。Bazel也没有办法为运行命令传入环境变量,所以在这种情况下,我们可以在运行目标之前对现有的.env 文件进行源化。

我们的脚本

下面是这个脚本的样子(为了清晰起见,略作删减)。

#!/usr/bin/env ruby

ENV_FILE = ".env"

def main
  usage unless ARGV.size > 0

  case ARGV.first
  when "build"
    build(ARGV[1..-1])
  when "test"
    test(ARGV[1..-1])
  when "run"
    run(ARGV[1..-1])
  else
    usage
  end
end

def usage
  puts "Usage: ./bazel (build|test|run) <args>"
  exit 1
end

def build(targets)
  puts_and_exec "bazel build #{build_targets(targets)}"
end

def test(targets)
  test_envs = env_variables.map { |e| "--test_env #{e}"}.join(" ")
  puts_and_exec "bazel test #{test_envs} #{build_targets(targets)}"
end

def run(args)
  puts_and_exec "bash -c 'set -a; source #{ENV_FILE}; bazel run #{args.join(" ")}'"
end

def env_variables
  File.readlines(ENV_FILE).map do |line|
    key, value = line.strip.split("=", 2)
    "#{key}=#{ENV[key] || value}"
  end
end

def build_targets(targets)
  targets.empty? ? "//..." : targets.join(" ")
end

def puts_and_exec(command)
  puts command
  exec command
end

main if __FILE__ == $0

"#{key}=#{ENV[key] || value}" 部分允许环境覆盖.env 文件中的值。这让我们可以做这样的事情。

FOO=bar ./bazel run ...