Firebase-tools: 接受函数的 JSON 文件:config:set

创建于 2017-07-20  ·  37评论  ·  资料来源: firebase/firebase-tools

目前,可以使用firebase functions:config:get将整个函数配置为 JSON 文件,但是我们必须在使用firebase functions:config:set命令时手动内联每个变量,这使得它在处理时几乎无法使用不同的环境。

理想情况下,我们应该能够做到:

firebase functions:config:get > config.json
firebase functions:config:set -f config.json

最有用的评论

感谢您的分享! @劳伦兹隆
我想从文件导入,所以我使用了这样的命令。

firebase functions:config:set service_account="$(cat service-account.json)"

我将服务帐户 json 文件导入到 Firebase Cloud Functions,因为我想使用 Firebase Admin SDK 的用户管理 API。

所有37条评论

我同意@jpreynat ,我们害怕在部署到我们的生产之前忘记设置配置config:set绑定到我们的自定义部署脚本中将是一个很好的添加功能。
现在是否有任何解决方法,也许在配置中进行管道传输?

似乎您正在尝试跨项目复制配置。 firebase functions:config:clone对你有用吗?

为了提供一些上下文,我们有意远离基于文件的配置,因为我们认为它鼓励不良做法(即,保留一个充满机密的文件与您的代码一起存储,甚至可能签入您的源存储库)。

你会怎么想(机制待定)来声明“必需的”配置变量——如果你试图在当前项目中没有必需的配置的情况下进行部署,它会在任何更改之前出错。 这能解决您的主要顾虑吗? 如果没有,为什么拥有一个对您特别有用的文件?

@mbleigh这正是我不知道我想要的。 好主意!
可能是“机械待定”的给定/您的意思,但是:在我们项目中的某个文件中而不是远程定义密钥/配置结构,以便它像其他任何事情一样通过 git/PR 审查过程。
@jpreynat很抱歉劫持问题

@laurenzlong感谢您的建议,但我们不会尝试复制配置。
我们主要关注的是根据我们的部署阶段更新不同的项目配置。
所以基本上,我们每个环境都有一个配置文件(dev、staging 和 prod),并分别设置 Firebase 配置(对于 PR,推送 master 和标签)。

@mbleigh感谢您提供的信息。
但是,我们并不确定如何以另一种方式存储所有这些环境而不是使用配置文件。
为了便于阅读,它们目前被格式化为 JSON,但我们正在考虑将它们格式化为键/值对,以将它们直接传递给functions:config:set命令。
我同意你的观点,这并不比传递 JSON 文件更安全。
除了使用文件之外,您对我们如何为不同阶段存储环境变量有什么建议吗?

@ahaverty不用担心,我很高兴可以在这里讨论这个问题。

@jpreynat当您运行functions:config:set它会与当前项目一起存储。 如果您使用firebase use在项目之间切换,您的配置将是持久的。 因此,您可以set每个项目运行一直存在。 这应该解决“不同环境的不同配置”问题,所以也许我不了解真正的问题?

@jpreynat我现在要关闭这个问题,但如果迈克尔和我误解了你的要求,请重新打开。

@laurenzlong @mbleigh对不起,我没有在这个线程上快速回答。
我们暂时找到了解决此问题的方法,但这里有一些上下文可以澄清我的请求:

我们正在使用 Travis 部署我们的应用程序(请注意,这对于任何 CI 甚至本地都很好)。 由于我们根据分支和标签将我们的应用程序部署到不同的阶段,因此能够根据环境使用不同的 JSON 文件会很棒。
例如,这将允许根据此条件执行firebase config:set -f staging.jsonfirebase config:set -f prod.json
我们的解决方法是使用firebase作为节点模块,但我们仍然需要内联我们的 JSON 配置,这很容易出错。

@jpreynat我仍然对你的回答有些困惑。 一旦你设置了一次配置,它就会一直保持在那里,即使是跨部署,除非你通过运行 firebase functions:config :unset 来明确取消设置配置值。 所以没有必要把它作为 CI 过程的一部分。

有人可以重新打开这个吗? %) @jpreynat @mbleigh这确实是缺少的东西之一。 Atm 我有将近 15 个配置变量(还有更多),如果functions:config:set可以以某种方式接受 JSON,那将非常方便。

我们对@mbleigh关于“声明”必需的”配置变量的建议投了大票——如果你试图在当前项目中没有必需的配置的情况下进行部署,它会在任何更改之前出错。”
也成为我们的痛点,但这将解决我们所有的问题,并且非常适合版本控制/审查过程👍

https://medium.com/@AllanHasegawa/setting -config-for-firebase-cloud-functions-with-json-136f455e7c69

@yaronyosef您可以直接向functions:config :set 命令提供 JSON,只是您必须以适合您平台的方式对其进行格式化。 对于 Linux,以下应该有效:

firebase functions:config:set foo='{"bar":"something","faz":"other"}'

感谢您的分享! @劳伦兹隆
我想从文件导入,所以我使用了这样的命令。

firebase functions:config:set service_account="$(cat service-account.json)"

我将服务帐户 json 文件导入到 Firebase Cloud Functions,因为我想使用 Firebase Admin SDK 的用户管理 API。

很酷的用例! 感谢分享!

我们也更喜欢通过 Git 跟踪非秘密配置,其中提交的 JSON 运行时配置文件(针对多个环境)将具有权威性,然后 CI 环境将从对应于特定环境的文件中应用functions:config:set (和每个环境对应一个单独的 Firebase 项目)。

如果您将配置保存在 git 中,那么将它们读入您的函数运行时就很简单了。 只需将它们放在configs/<project_id>.json ,然后:

let config = {};
try {
  config = require(`./configs/${process.env.GCLOUD_PROJECT}.json`);
} catch (e) {
  console.log('No config found for project', process.env.GCLOUD_PROJECT);
}

Firebase 使用的运行时配置专门设计用于避免将配置签入 git,因为(尤其是使用机密)我们发现它是一种导致问题多于解决的模式。

确实如此,尽管后来我们根本不再为此目的使用 Config API。 仍然通过 Config API 来存储敏感值(这似乎是一个合适的用例,基于 Firebase 文档)并以完全相同的方式加载源控制和未跟踪的值可能是有益的(即通过config()firebase-functions )。

目前,我正在使用以下单行将整个.runtimeconfig送入functions:config:set

firebase functions:config:set $(jq -r 'to_entries[] | [.key, (.value | tojson)] | join("=")' < .runtimeconfig/${FIREBASE_ENV}.json)

其中FIREBASE_ENV是部署中使用的环境的名称(例如dev )。 能够进一步缩短它会很好,例如

firebase functions:config:set -f .runtimeconfig/${FIREBASE_ENV}.json

不过,最终,如果 Config API 启用了所有更改的简单易用的审计跟踪,即使这样也可能没有必要(如果已经是这种情况,请纠正我,我想探讨一下)。

那么这是怎么回事呢? 😄

希望看到@mbleigh 的建议仍然得到实施! 我们经常遇到忘记在非开发目标上设置配置的问题

感谢您的线程碰撞。 还没有什么要报告的,但我会在我们的计划会议上再次提出这个问题,看看我们是否可以开始在这方面取得一些进展。

我使用 JSON 架构文件验证我的配置,以确保它具有我期望的所有值。 此验证是我的 CI 管道的一部分,如果失败则阻止部署。 如果像我一样使用 TypeScript 编写函数,则可以从类型生成 JSON 模式。

它工作得很好,但功能 config 只接受字符串的限制,因此架构无法验证配置值是否为数字。 对于布尔值,这是可以的,因为我可以使用"true""false"作为唯一有效值的枚举。

请参阅我的 CircleCI 配置此博客文章以供参考。

@hgwood感谢分享! 我们肯定也会将此添加到我们的 CI 中。

只是想留下我对这个“问题”的解决方案,以及我对讨论的看法:

  1. 我知道秘密/敏感值不应该存储在 repo 中。 我的解决方案仅适用于非秘密值。 秘密值是手动设置的,但是根据需要标记配置值的方法对于这些秘密值来说会很棒。
  2. 我认为这比某些人认为的更重要。 必须为不同的部署环境项目手动设置单独的配置值,并且不能轻松地同时查看/比较它们是一个 PITA。 在文件中定义配置可以让这个过程减少很多麻烦。
  3. 我认为将功能配置编辑器添加到 Web 控制台会大大改善这一点,但是如果有一种方法可以同时查看多个项目的配置,那就太好了,这样您就可以轻松比较不同部署环境的配置。
  4. 我一直在说 firebase 需要允许在单个项目中定义环境,因此您不需要将多个项目用作部署环境。 如果存在这种情况,那么在单个项目中同时查看多个环境的配置将非常容易。
  5. 我的解决方案可能有问题,不要逐字相信我的代码,但我确实喜欢一般概念。

我的解决方案

我使用级联 yaml 配置文件,这对于适用于所有配置的默认值和值非常有用,而不管部署环境如何。

./config/default.yml

app:
  name: My App
featureFlags:
  someFeature:
    enabled: false

./config/dev.yml (其他部署环境的类似配置文件)

imports:
  - {resource: default.yml}
app:
  environmentName: Development
services:
  someOtherService:
    baseUrl: https://dev.someotherservice.com/api
featureFlags:
  someFeature:
    enabled: true

然后,我编写了一个加载解析配置的小节点脚本,并返回一个以空格分隔的 key=val 对列表

yaml_to_keyval.ts

import * as configYaml from "config-yaml"
import * as flat from "flat"


const yamlFile = process.argv[2]
const config = configYaml(yamlFile)
const flatConfig = flat(config)

const key_val_list = Object.keys(flatConfig).map(function(key){
    return `${key}=${flatConfig[key]}`
})

console.log(key_val_list.join(' '))

在 CI 部署期间,我使用这个 bash shell 脚本来获取 key=val 对并使用functions:config :set 设置它们

# env is set to the desired deployment environment (eg "dev"), which is passed as an argument to the CI script command
config_in="$(readlink -f ./config/$env.yml)"
key_vals="$(ts-node ./scripts/node/yaml_to_keyval.ts $config_in)"
firebase functions:config:set $key_vals

@kanemotos我喜欢您的cat解决方案,但是您在尝试上传证书文件时没有遇到Precondition check failed错误吗?

由于这些原因,我们最终使用了部署管理器。

我们通过“config Yaml”为DM设置所有配置。 然后 DM 创建适当的运行时配置和变量,并且firebase deploy在部署时检索和设置它们的值,而根本不必使用firebase config:set 。 这也允许我们设置其他资源(例如存储桶及其访问控制)并在 RC 中设置它们的值,而无需在模板外部提供它们。 此外,这允许我们使用camelCase配置和变量名称。

我们在外部设置值的唯一时间是当我们需要通过运行时配置传递(加密的)服务帐户密钥时,因为 DM 无法安全地加密值。 但是我们仍然使用gcloud beta runtime-config configs variables set (在 DM 部署之后和firebase deploy )而不是firebase config:set ,因为前者接受来自 KMS 的标准输出。

我只是想补充一下 - 通读这篇文章,似乎有些人错过了导致一些混乱的关键点。 如果这也在文档中被清除,那将非常有用。

基本上, functions.config()在 _deployments_ 之间持续存在,但也可以在不同的 firebase 项目之间单独设置!

这意味着对于 dev、staging 和 prod 环境,您必须设置多个相同的 firebase 项目(而不是使用多站点托管或部署到相同的项目...... .

一旦你设置了别名(或者只是在项目之间切换firebase use ),你就可以将同一个 repo部署到多个单独的 firebase 项目。 这就是您设置开发环境、暂存和生产的方式 - 作为单独的 Firebase 项目。

然后,您可以为每个设置单独的环境配置,如下所示:
firebase -P dev config:set - 为你的开发环境设置变量
firebase -P prod config:set - 为您的生产环境设置变量...

然后,您可以两次部署同一个 repo,如下所示:
firebase deploy -P dev
firebase deploy -P prod
它们都将部署到各自的 Firebase 项目,并将包含自己的独立环境配置,这些配置将在部署之间持续存在。

是的,我们也是这样做的。 开发/测试/生产的单独项目。

只是分享我的 vanilla JS 解决方案,它适用于您在哪个平台上开发(Windows、MacOS、Linux):

为 dev/test/prod 使用单独的项目。

/.firebaserc

{
  "projects": {
    "dev": "my-project-name-dev",
    "test": "my-project-name-test",
    "prod": "my-project-name-prod"
  }
}

配置文件

/functions/config文件夹中的 JSON 文件:

/functions/config/config.dev.json 
/functions/config/config.test.json 
/functions/config/config.prod.json 
/functions/config/config.SAMPLE.json <-- only file tracked in git

Example content:
{
    "google": {
        "key": "my-api-key",
        "storage_bucket": "firebase-storage-bucket"
    },
    "another_vendor": {
        "my_prop": "my value"
    },
    ...
}

/functions/set-config.js

const fs = require('fs');
const env = process.argv[2];

const configPath = `./config/config.${env}.json`;

if (!(configPath && fs.existsSync(configPath))) {
    return;
}

const collectConfigLines = (o, propPath, configLines) => {
    propPath = propPath || '';
    configLines = configLines || [];
    for (const key of Object.keys(o)) {
        const newPropPath = propPath + key;
        if (typeof o[key] === 'object') {
            collectConfigLines(o[key], newPropPath + '.', configLines);
        } else if (o[key] != null && o[key] !== '') {
            configLines.push(`${newPropPath}=${JSON.stringify(o[key])}`);
        }
    }
}

const config = require(configPath);
const configLines = [];
collectConfigLines(config, '', configLines);

const cp = require('child_process');
cp.execSync(`firebase -P ${env} functions:config:set ${configLines.join(' ')}`);

/functions/package.json

...
"scripts": {
    "config:set:dev": "node set-config dev",
    "config:set:test": "node set-config test",
    "config:set:prod": "node set-config prod",
    ...
},
...

如果有人还在看,这篇中篇文章中使用的方法https://medium.com/indielayer/deploying-environment-variables-with-firebase-cloud-functions-680779413484。 救了我。

我个人缺少的是在进行部署的

此外,如果我们想稍后更改现有变量,并且正在开发某些功能,那么现在您可以检出最后发布的标签,构建它,部署,检出回到您的功能分支,继续。 这真的使这个无法使用。

基本上我们想要云功能的远程配置

@bezysoftware我认为您需要使用环境变量(而不是环境配置),您可以跳过 firebase 层并在谷歌云平台上进行,查看https://cloud.google.com/functions/docs/env-无功

感谢您的分享! @劳伦兹隆
我想从文件导入,所以我使用了这样的命令。

firebase functions:config:set service_account="$(cat service-account.json)"

我将服务帐户 json 文件导入到 Firebase Cloud Functions,因为我想使用 Firebase Admin SDK 的用户管理 API。

它没有用

@Md-Abdul-Halim-Rafi

firebase功能:config :set service_account="$(cat service-account.json)"

为我工作,我可以看到firebase functions:config:get的配置变量。
第一次没有工作,因为我不在项目文件夹中,也没有选择要设置配置变量的 firebase 项目,选择 firebase 项目使用firebase use --add ,CLI 会提示你Firebase 控制台上的项目列表(假设您使用firebase login登录 CLI)

@dnhyde

firebase功能:config :set service_account="$(cat service-account.json)"

在您的 json 文件中使用除字符串(例如布尔值或数字)之外的任何其他值类型时,这似乎不起作用:

{
   "test": {
        "hmm": true
    }
}

失败:

Error: HTTP Error: 400, Invalid value at 'variable.text' (TYPE_STRING), true

在我看来,我应该能够做到:

firebase functions:config:get > config.json

然后:

firebase functions:config:set < config.json

让这两个命令互补才有意义。

将配置放在文件中允许我对其进行版本控制并查看它随时间的变化情况。

不幸的是,我们现在必须完成的唯一方法(使用env=$(cat config.json) )也会导致我无法让 config.json 成为实际值,因为我无法将它们包装在{ env: ... }

自己注意:这是我需要开始功能拉取请求的地方: https :

只是分享我的 vanilla JS 解决方案,它适用于您在哪个平台上开发(Windows、MacOS、Linux):

为 dev/test/prod 使用单独的项目。

/.firebaserc

{
  "projects": {
    "dev": "my-project-name-dev",
    "test": "my-project-name-test",
    "prod": "my-project-name-prod"
  }
}

配置文件

/functions/config文件夹中的 JSON 文件:

/functions/config/config.dev.json 
/functions/config/config.test.json 
/functions/config/config.prod.json 
/functions/config/config.SAMPLE.json <-- only file tracked in git

Example content:
{
    "google": {
        "key": "my-api-key",
        "storage_bucket": "firebase-storage-bucket"
    },
    "another_vendor": {
        "my_prop": "my value"
    },
    ...
}

/functions/set-config.js

const fs = require('fs');
const env = process.argv[2];

const configPath = `./config/config.${env}.json`;

if (!(configPath && fs.existsSync(configPath))) {
    return;
}

const collectConfigLines = (o, propPath, configLines) => {
    propPath = propPath || '';
    configLines = configLines || [];
    for (const key of Object.keys(o)) {
        const newPropPath = propPath + key;
        if (typeof o[key] === 'object') {
            collectConfigLines(o[key], newPropPath + '.', configLines);
        } else if (o[key] != null && o[key] !== '') {
            configLines.push(`${newPropPath}=${JSON.stringify(o[key])}`);
        }
    }
}

const config = require(configPath);
const configLines = [];
collectConfigLines(config, '', configLines);

const cp = require('child_process');
cp.execSync(`firebase -P ${env} functions:config:set ${configLines.join(' ')}`);

/functions/package.json

...
"scripts": {
    "config:set:dev": "node set-config dev",
    "config:set:test": "node set-config test",
    "config:set:prod": "node set-config prod",
    ...
},
...

只是分享我的 vanilla JS 解决方案,它适用于您在哪个平台上开发(Windows、MacOS、Linux):

为 dev/test/prod 使用单独的项目。

/.firebaserc

{
  "projects": {
    "dev": "my-project-name-dev",
    "test": "my-project-name-test",
    "prod": "my-project-name-prod"
  }
}

配置文件

/functions/config文件夹中的 JSON 文件:

/functions/config/config.dev.json 
/functions/config/config.test.json 
/functions/config/config.prod.json 
/functions/config/config.SAMPLE.json <-- only file tracked in git

Example content:
{
    "google": {
        "key": "my-api-key",
        "storage_bucket": "firebase-storage-bucket"
    },
    "another_vendor": {
        "my_prop": "my value"
    },
    ...
}

/functions/set-config.js

const fs = require('fs');
const env = process.argv[2];

const configPath = `./config/config.${env}.json`;

if (!(configPath && fs.existsSync(configPath))) {
    return;
}

const collectConfigLines = (o, propPath, configLines) => {
    propPath = propPath || '';
    configLines = configLines || [];
    for (const key of Object.keys(o)) {
        const newPropPath = propPath + key;
        if (typeof o[key] === 'object') {
            collectConfigLines(o[key], newPropPath + '.', configLines);
        } else if (o[key] != null && o[key] !== '') {
            configLines.push(`${newPropPath}=${JSON.stringify(o[key])}`);
        }
    }
}

const config = require(configPath);
const configLines = [];
collectConfigLines(config, '', configLines);

const cp = require('child_process');
cp.execSync(`firebase -P ${env} functions:config:set ${configLines.join(' ')}`);

/functions/package.json

...
"scripts": {
    "config:set:dev": "node set-config dev",
    "config:set:test": "node set-config test",
    "config:set:prod": "node set-config prod",
    ...
},
...

抱歉,您什么时候运行脚本“ config:set :dev”? 我不明白……谢谢!

此页面是否有帮助?
0 / 5 - 0 等级