如何在package.json中使用环境变量

121
因为我们不想把敏感数据包含在项目代码中,包括 package.json 文件,所以在我看来使用环境变量是一个合乎逻辑的选择。
示例 package.json:
  "dependencies": {
    "accounting": "~0.4.0",
    "async": "~1.4.2",
    "my-private-module":"git+https://${BB_USER}:${BB_PASS}@bitbucket.org/foo/bar.git"

这可行吗?

问题并不在于这是否明智或者是不好的,而仅在于是否可能


1
你找到这个问题的解决方案了吗? - Goca
10个回答

71

如果您使用.env文件,请使用grepeval从.env文件中获取环境变量的值。

如@Paul建议的,更新了start2

"scripts": {
    "start": "NODE_ENV=$(grep NODE_ENV .env | cut -d '=' -f2) some_script",
    "start2": "eval $(grep '^NODE_ENV' .env) && some_script"
}

这应该是顶级评论,因为它完美解决了这个问题。 - Marko Rochevski
1
你也可以将其缩短为 eval $(grep '^NODE_ENV' .env) && some_script - Paul
16
这有两个问题。1:问题是关于依赖关系而不是脚本的。脚本在shell中运行,因此在脚本中使用环境变量与在普通的package.json值中使用环境变量非常不同。2:(也许我错过了什么,但是)通过grep获取环境变量的值似乎是一个复杂的方法, 只需执行 $NODE_ENV 就可以了。此外,如果NODE_ENV已经被定义为环境变量,则不需要重新定义。看起来只是节省您执行 source .env 的时间(您可以使用类似direnv的东西自动为您执行它)。 - Henry Woody
有没有办法像这样链接它们:eval $(grep -Ev '(^AWS_PROFILE|^AWS_REGION)' .env),这样我就不必这样做了:eval $(grep '^AWS_PROFILE' .env) && eval $(grep '^AWS_REGION' .env) - timhc22
2
震惊这篇文章有这么多的赞。使用eval来执行文件内容是一个非常糟糕的想法。希望在一些恶意行为者注入他们的.env文件,比如mysql -e "DROP DATABASE prod;"; NODE_ENV="123"到你公司的构建流程之前,你能找到新工作。 - joshuakcockrell
显示剩余4条评论

22
我有类似但不同的需求。对我来说,我想在脚本中使用环境变量。
我不直接在package.json中使用环境变量,而是这样做:
"some-script": "./scripts/some-script.sh",

而在some-script.sh中:

#!/bin/sh

source .env

npm run some-other-script -- --prop=$SOME_ENV_VAR

3
你能告诉我如何在“some-other-script”中访问“prop”吗? - Robert Skarżycki
1
好久不见了。你能按照这里最受欢迎的答案来操作吗?https://dev59.com/DG035IYBdhLWcg3wTuNu - techguy2000
bash脚本在Windows上不能运行,显示'.'不是内部或外部命令,也不是可运行的程序或批处理文件。 - Tomasz Juszczak
我需要添加source .env,并且它按预期工作。谢谢。 - Kostanos

10

以下是我如何绕过package.json来达到相同目的的方法。它使用一个脚本,从package.json的自定义部分读取URL模块,在其中插入环境变量,并用npm install --no-save安装它们(根据用例,可以省略--no-save)。

额外奖励:它尝试从.env.json中读取环境变量,该文件可以被gitignore,对于开发非常有用。

  1. 创建一个将从package.json的自定义部分读取的脚本

env-dependencies.js

const execSync = require('child_process').execSync
const pkg = require('./package.json')

if (!pkg.envDependencies) {
  return process.exit(0)
}

let env = Object.assign({}, process.env)

if (typeof pkg.envDependencies.localJSON === 'string') {
  try {
    Object.assign(env, require(pkg.envDependencies.localJSON))
  } catch (err) {
    console.log(`Could not read or parse pkg.envDependencies.localJSON. Processing with env only.`)
  }
}

if (typeof pkg.envDependencies.urls === 'undefined') {
  console.log(`pkg.envDependencies.urls not found or empty. Passing.`)
  process.exit(0)
}

if (
  !Array.isArray(pkg.envDependencies.urls) ||
  !(pkg.envDependencies.urls.every(url => typeof url === 'string'))
) {
  throw new Error(`pkg.envDependencies.urls should have a signature of String[]`)
}

const parsed = pkg.envDependencies.urls
  .map(url => url.replace(/\${([0-9a-zA-Z_]*)}/g, (_, varName) => {
    if (typeof env[varName] === 'string') {
      return env[varName]
    } else {
      throw new Error(`Could not read env variable ${varName} in url ${url}`)
    }
  }))
  .join(' ')

try {
  execSync('npm install --no-save ' + parsed, { stdio: [0, 1, 2] })
  process.exit(0)
} catch (err) {
  throw new Error('Could not install pkg.envDependencies. Are you sure the remote URLs all have a package.json?')
}
  1. 在你的package.json文件中添加"postinstall": "node env-dependencies.js",这样它将在每次npm install时运行。

  2. 使用你想要的URL将私有git仓库添加到package.json中(注意:它们都必须在根目录下有一个package.json文件!):

"envDependencies": {
  "localJSON": "./.env.json",
  "urls": [
    "git+https://${GITHUB_PERSONAL_ACCESS_TOKEN}@github.com/user/repo#semver:^2.0.0"
  ]
},

semver的规范符号#semver:^2.0.0可以省略,但它指的是一个Git标签,这非常有用,因为它使得你的Git服务器成为了一个完整的包管理器。

  1. npm install

7
不可以用该方法。您应该使用“git+ssh”访问存储库,并将私钥存储在“~/.ssh”中。然后,您的命令行应该如下所示:
"my-private-module":"git+ssh://git@bitbucket.org/foo/bar.git"

这里没有包含任何敏感信息。


此外,您甚至可以为此目的使用不同的ssh密钥和ssh配置,而不是通常的id_rsa。 - Zlatko
是的,我会推荐那样做。(我确实是指“存储一个私钥”,但可能需要更清晰明了。) - Steve Bennett
谢谢!是的,这是Heroku :-S。所以我想应该是自定义构建包。最终我认为Docker将成为终极环境。需要做到这一点!问候! - kaasdude
这个 .npmrc 文件可以解释环境变量!但是它不能组合,我认为不适合用于这些目的... - kaasdude

5

不可能,因为npm不将任何字符串值视为任何类型的模板。

如果您的提供程序支持,请使用git+ssh和ssh代理可能更好。


任何使用Docker进行部署的人都不应该使用这种方法。 - saurabh
@monothorn - 有什么更好的方法可以做到这一点吗?你有什么建议...我还在想,如果我们不使用ssh,会有什么更好的方法。 - Ashmah
@Ashmah HTTPS是正确的选择,但你需要限制访问所生成的令牌,并确保仓库是私有的。除此之外,你还需要了解是否存在其他安全问题。 - saurabh

4
我有同样的需求,我的解决方案基于@Long Nguyen的回答。这样,我只能依赖于.env文件中定义的内容。
.env
...
SKIP_PREFLIGHT_CHECK=true
...

package.json

...
"scripts": {
  "test": "yarn cross-env $(grep SKIP_PREFLIGHT_CHECK ../../.env) react-app-rewired test --watchAll=false"
}
...

2
您可以使用环境变量注入到您的package.json中,就像这样:

任何以npm_config_开头的环境变量都会被解释为配置参数。例如,将npm_config_foo = bar放在环境中将把foo配置参数设置为bar。没有给定值的任何环境配置都将被赋予true值。配置值不区分大小写,因此NPM_CONFIG_FOO = bar将起作用。

https://docs.npmjs.com/misc/config#environment-variables

9
这是用于设置变量以供npm脚本使用的-类似于package.json中的配置部分。这些变量无法被package.json读取。 - kgiannakakis

2
你可以安装包https://www.npmjs.com/package/env-cmd,并且你的所有环境变量都将被显示,例如:.env文件中的内容。
ENV1=THANKS
ENV2=FOR ALL
ENV3=THE FISH

Package.json:

"scripts": {
    "test": "env-cmd pact-broker can-i-deploy --broker-token=${ENV1}"
  }

举个你提出的问题中的另一个例子:

    "my-private-module":"env-cmd git+https://${BB_USER}:${BB_PASS}@bitbucket.org/foo/bar.git"

2
env-cmd README中可以看到:"你必须使用\来转义$字符,否则你的终端可能会在传递给env-cmd之前尝试自动展开它"。他给出了一个npm脚本的例子:env-cmd -x node index.js --arg=\\$VAR。还可以参考https://github.com/toddbluhm/env-cmd-examples/blob/master/examples/env-file-env-expansion/package.json。 - icc97
1
有趣,谢谢 :) 我稍后会查看这个 - Darex1991

1

如果你正在 Docker 容器中运行 Node

使用 Docker Compose 注入环境变量

app:
  environment:
    - NODE_ENV=staging

在Dockerfile中运行你的package.json脚本

CMD [ "npm", "run", "start" ]

使用 echoprintenv

 "scripts": {
    "start": "node -r dotenv/config app.js dotenv_config_path=/run/secrets/$(echo $NODE_ENV)"
    "start": "node -r dotenv/config app.js dotenv_config_path=/run/secrets/$(printenv NODE_ENV)"
}

不要将此用于敏感环境变量。这是指向 Docker 机密文件的一种非常好的方式(就像此示例所示)。


也可以在没有 Docker 容器的情况下使用(适用于我)。 - firelynx

0

对于复杂的环境变量,您可以使用https://stedolan.github.io/jq/来访问JSON文件(在您的情况下是env文件)。 JSON文件可能类似于以下内容:

{
    "env" :
     {  
            "username" : "1345345",
            "Groups" :  [],
            "arraytest" : [
               {
                  "yes" : "1",
                  "no" : "0"
               }
             ]
     }
}

所以脚本可能是像这样的,来访问yes值

"scripts": {
    "yes": "jq [].arraytest[0].yes?"
}    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接