Firebase-tools: Aceite o arquivo JSON para as funções: config: set

Criado em 20 jul. 2017  ·  37Comentários  ·  Fonte: firebase/firebase-tools

Atualmente, é possível obter toda a configuração das funções como um arquivo JSON usando firebase functions:config:get , mas temos que embutir manualmente cada variável ao usar o comando firebase functions:config:set , o que o torna praticamente inutilizável ao trabalhar em ambientes diferentes.

Idealmente, devemos ser capazes de fazer:

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

Comentários muito úteis

Obrigado por compartilhar! @laurenzlong
Eu queria importar de um arquivo, então usei um comando como este.

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

Importei um arquivo json de conta de serviço para o Firebase Cloud Functions porque queria usar a API de gerenciamento de usuários do SDK Admin do Firebase.

Todos 37 comentários

Concordo com @jpreynat , config: set em nossos scripts de implementação customizados com um config.json seria um bom recurso de adicionar.
Há alguma solução alternativa agora, talvez canalizar a configuração?

Parece que você está tentando replicar a configuração entre projetos. firebase functions:config:clone funcionaria para você?

Para dar algum contexto, nos afastamos intencionalmente da configuração baseada em arquivo porque achamos que incentiva práticas ruins (ou seja, manter um arquivo cheio de segredos armazenados com seu código, potencialmente até mesmo verificado em seu repositório de origem).

O que você pensaria sobre alguma maneira (TBD de mecânica) de declarar variáveis ​​de configuração "obrigatórias" - e se você tentasse implantar sem a configuração necessária no projeto atual, ocorreria um erro antes que qualquer coisa fosse alterada. Isso resolveria suas principais preocupações? Se não, por que ter um arquivo especificamente útil para você?

@mbleigh Isso é exatamente o que eu não sabia que queria. Boa ideia!
Provavelmente um dado / o que você quis dizer com 'Mecânica TBD', mas: Definir a estrutura keys / config em algum arquivo dentro de nosso projeto ao invés de remotamente, para que passe pelo processo de revisão git / PR como todo o resto.
@jpreynat Desculpe pelo problema de sequestro

@laurenzlong Obrigado pelo conselho, mas não estamos tentando duplicar uma configuração.
Nossa principal preocupação é atualizar a configuração de diferentes projetos, dependendo do nosso estágio de implantação.
Então, basicamente, temos um arquivo de configuração por ambiente (dev, staging e prod) e definimos a configuração do Firebase respectivamente (para PRs, push on master e tags).

@mbleigh Obrigado pela informação.
No entanto, não temos certeza de como podemos armazenar todos esses envs de outra maneira que não usando arquivos de configuração.
Eles estão atualmente formatados como JSON para simplificar a leitura, mas estamos pensando em formatá-los como pares de chave / valor para passá-los diretamente para o comando functions:config:set .
Concordo com você que isso não é mais seguro do que passar um arquivo JSON.
Você tem alguma recomendação sobre como podemos armazenar nossas variáveis ​​de ambiente para diferentes estágios além do uso de arquivos?

@ahaverty Não se preocupe, estou feliz que isso possa ser debatido aqui.

@jpreynat quando você executa functions:config:set ele é armazenado com o projeto atual. Se você estiver usando firebase use para alternar entre projetos, sua configuração será persistente. Portanto, você pode executar set uma vez por projeto e ele sempre estará lá. Isso deve resolver o problema de "configuração diferente para ambientes diferentes", então talvez eu não esteja entendendo o problema real?

@jpreynat Vou encerrar este problema por enquanto, mas

@laurenzlong @mbleigh Desculpe não respondi rapidamente neste tópico.
Encontramos uma solução alternativa para esse problema por enquanto, mas aqui está algum contexto para esclarecer minha solicitação:

Estamos implantando nosso aplicativo usando o Travis (observe que isso seria ótimo com qualquer CI ou mesmo localmente). Como implantamos nosso aplicativo em diferentes estágios, dependendo do branch e da tag, seria ótimo poder usar um arquivo JSON diferente com base no ambiente.
Por exemplo, isso permitiria fazer firebase config:set -f staging.json ou firebase config:set -f prod.json dependendo desta condição.
Nossa solução alternativa foi usar firebase como um módulo de nó, mas ainda temos que embutir nossa configuração JSON, que está sujeita a erros.

Ei, @jpreynat , ainda estou um pouco confuso com sua resposta. Depois de definir a configuração uma vez, ela sempre permanece lá, mesmo entre as implantações, a menos que você explicitamente cancele a definição dos valores de configuração executando as funções do firebase

Alguém pode reabrir isso? %) @jpreynat @mbleigh Essa é realmente uma das coisas que está faltando. Atm, tenho cerca de 15 variáveis ​​de configuração (e mais vindo) e isso será muito útil se functions:config:set puder aceitar JSON de alguma forma.

Grande voto de nós para a sugestão de @mbleigh sobre "declarar" variáveis ​​de configuração obrigatórias - e se você tentasse implementar sem a configuração necessária no projeto atual, ocorreria um erro antes de qualquer coisa ser alterada. "
Se tornando um ponto problemático para nós também, mas isso resolveria todos os nossos problemas e se encaixaria perfeitamente no processo de controle / revisão de versão 👍

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

Ei @yaronyosef Você pode fornecer JSON diretamente para as funções do firebase

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

Obrigado por compartilhar! @laurenzlong
Eu queria importar de um arquivo, então usei um comando como este.

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

Importei um arquivo json de conta de serviço para o Firebase Cloud Functions porque queria usar a API de gerenciamento de usuários do SDK Admin do Firebase.

Caso de uso legal! Obrigado por compartilhar!

Também preferiríamos rastrear configurações não secretas por meio do Git, onde os arquivos de configuração de tempo de execução JSON (para vários ambientes) serão autoritativos e o ambiente de CI aplicará functions:config:set do arquivo correspondente a um ambiente específico (e cada ambiente corresponde a um projeto separado do Firebase).

Se você mantiver os configs no git, é trivial lê-los no tempo de execução de suas funções. Basta colocá-los em configs/<project_id>.json e então:

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

O config de tempo de execução usado pelo Firebase é projetado especificamente para evitar que o config seja verificado no git, já que (especialmente com segredos) descobrimos que ele é um padrão que causa mais problemas do que resolve.

Isso é verdade, embora não usemos mais a API de configuração para esse propósito. Pode ser benéfico continuar usando a Config API para armazenar valores confidenciais (que parece ser um caso de uso apropriado, com base nos documentos do Firebase) e carregar valores controlados pela origem e não rastreados da mesma maneira exata (ou seja, por meio de config() fornecido por firebase-functions ).

Por enquanto, estou usando a seguinte linha única para alimentar todos os .runtimeconfig para functions:config:set :

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

onde FIREBASE_ENV é o nome do ambiente usado na implantação (por exemplo, dev ). Seria bom ser capaz de encurtar ainda mais, por exemplo

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

Em última análise, porém, mesmo isso pode não ser necessário se a API de configuração habilitou trilhas de auditoria simples de usar de todas as alterações (corrija-me se esse já for o caso, eu gostaria de explorar isso).

Então, como isso está indo? 😄

Adoraria ver a sugestão @mbleigh implementada ainda! Estamos constantemente tendo problemas para esquecer de definir configurações em nossos destinos não-dev

Obrigado pelo aumento do fio. Nada a relatar ainda, mas vou trazer isso à tona novamente em nossas reuniões de planejamento para ver se podemos começar a fazer algum progresso nisso.

Valido minha configuração com um arquivo de esquema JSON para garantir que tenha todos os valores que espero. Essa validação faz parte do meu pipeline de CI e evita a implantação em caso de falha. Se, como eu, você usa TypeScript para escrever suas funções, pode gerar o esquema JSON a partir de seus tipos.

Funciona muito bem, com a limitação de que as funções config só aceitam strings, de modo que o esquema não pode validar que um valor de configuração seja um número. Para booleanos, está tudo bem porque posso usar um enum com "true" e "false" como os únicos valores válidos.

Veja minha configuração do CircleCI e esta postagem do blog para referência.

@hgwood obrigado por compartilhar! Com certeza iremos adicionar isso ao nosso CI também.

Só queria deixar minha solução para este "problema" e minhas reflexões sobre a discussão:

  1. Eu entendo que os valores secretos / confidenciais NÃO devem ser armazenados no repo. Minha solução é apenas para valores não secretos. Os valores secretos são definidos manualmente, mas uma maneira de marcar os valores de configuração conforme necessário seria excelente para esses valores secretos.
  2. Acho que isso é mais importante do que alguns parecem pensar. É um PITA ter que definir manualmente os valores de configuração individuais para diferentes projetos de ambiente de implantação e não ser capaz de visualizá-los / compará-los simultaneamente com facilidade. Definir configurações em arquivos torna o processo muito menos trabalhoso.
  3. Acho que adicionar um editor de configuração de funções ao console da web melhoraria muito isso, mas seria muito bom se houvesse uma maneira de visualizar simultaneamente as configurações de vários projetos, para que você pudesse comparar facilmente as configurações para diferentes ambientes de implantação.
  4. Sempre digo que o firebase precisa permitir que os ambientes sejam definidos em um único projeto, então você não precisa de vários projetos para usar como ambientes de implantação. Se isso existisse, seria muito fácil visualizar simultaneamente as configurações de vários ambientes em um único projeto.
  5. Minha solução provavelmente tem problemas, não confie no meu código literalmente, mas gosto do conceito geral.

Minha solução

Eu uso arquivos de configuração yaml em cascata, o que é útil para valores padrão e valores que se aplicam a todas as configurações, independentemente do ambiente de implementação.

./config/default.yml

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

./config/dev.yml (arquivos de configuração semelhantes para outros ambientes de implantação)

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

Em seguida, escrevi um pequeno script de nó que carrega a configuração resolvida e retorna uma lista delimitada por espaço de pares chave = 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(' '))

Durante a implantação do CI, uso este script bash shell para obter os pares key = val e defini-los com as funções: 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 Eu gosto de sua solução cat , mas você não encontrou um erro Precondition check failed ao tentar enviar seu arquivo cert?

Acabamos usando o Deployment Manager por esses motivos.

Definimos todas as configurações através de "config Yaml" para DM. Em seguida, o DM cria configurações e variáveis ​​de tempo de execução apropriadas e firebase deploy recupera e define seus valores no momento da implantação sem ter que usar firebase config:set todo. Isso também nos permite configurar outros recursos (por exemplo, baldes e seu controle de acesso) e definir seus valores em RC sem ter que fornecê-los externamente ao modelo. Além disso, isso nos permite usar camelCase config e nomes de variáveis.

A única vez em que definimos um valor externamente é quando precisamos passar uma chave de conta de serviço (criptografada) por meio da configuração do tempo de execução, porque o DM não pode criptografar valores com segurança. Mas ainda usamos gcloud beta runtime-config configs variables set para isso (após a implantação do DM e antes de firebase deploy ) e não firebase config:set , porque o primeiro aceita stdout do KMS.

Eu só queria acrescentar - lendo isto parece que algumas pessoas perderam um ponto crucial que leva a alguma confusão. Seria muito útil se isso também fosse esclarecido na documentação.

Basicamente, functions.config() persiste entre _deployments_, mas TAMBÉM pode ser definido separadamente entre diferentes projetos Firebase!

Isso significa que para ambientes de desenvolvimento, teste e produção, você deve configurar vários projetos Firebase idênticos (em vez de usar hospedagem de vários sites ou implantar no MESMO projeto ... 👎), cada um mantendo suas próprias - diferentes - variáveis ​​de ambiente .

Depois de definir aliases (ou apenas alternar entre projetos com firebase use ), você pode implantar o mesmo repo em vários projetos Firebase separados. É assim que você configura um ambiente de desenvolvimento, teste e produção - como projetos Firebase separados.

Em seguida, você pode definir configurações de ambiente separadas para cada um, como este:
firebase -P dev config:set - definir variáveis ​​para seu ambiente de desenvolvimento
firebase -P prod config:set - definir variáveis ​​para seu ambiente de produção ...

Em seguida, você pode implantar o mesmo repo duas vezes, assim:
firebase deploy -P dev
firebase deploy -P prod
e ambos serão implantados em seus respectivos projetos firebase e conterão suas próprias configurações de ambiente separadas que persistirão entre as implantações.

Sim, é assim que fazemos também. Projetos separados para dev / test / prod.

Apenas compartilhando minha solução JS vanilla que funciona em relação à plataforma em que você desenvolve (Windows, MacOS, Linux):

Usando projetos separados para dev / test / prod.

/.firebaserc

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

Arquivos de configuração

Arquivos JSON na pasta /functions/config :

/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",
    ...
},
...

Se alguém ainda estiver procurando, existe o método usado neste artigo médio https://medium.com/indielayer/deploying-environment-variables-with-firebase-cloud-functions-680779413484. Salvou a minha vida.

O que estou pessoalmente perdendo é a capacidade de definir uma variável de configuração sem fazer uma implantação. Temos um pipeline de CICD que faz a implantação em nossos diferentes ambientes e não queremos que um desenvolvedor construa o código localmente e o implante, especialmente na produção. É assim que as coisas quebram.

Além disso, se quisermos alterar uma variável existente posteriormente, com algum recurso sendo desenvolvido, agora você tem o checkout da última tag lançada, construí-la, implantar, fazer checkout de volta ao seu branch de recursos, continuar. Isso realmente o torna inutilizável.

Essencialmente, gostaríamos de configuração remota para funções de nuvem

@bezysoftware Acho que você precisa usar variáveis ​​de ambiente (em vez de configuração de ambiente), você pode pular a camada firebase e fazer isso na plataforma do Google Cloud, confira https://cloud.google.com/functions/docs/env- var

Obrigado por compartilhar! @laurenzlong
Eu queria importar de um arquivo, então usei um comando como este.

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

Importei um arquivo json de conta de serviço para o Firebase Cloud Functions porque queria usar a API de gerenciamento de usuários do SDK Admin do Firebase.

Não funcionou

@ Md-Abdul-Halim-Rafi

funções do firebase

Funcionou para mim, posso ver a var de configuração com firebase functions:config:get .
Não funcionou da primeira vez porque eu não estava na pasta do projeto e não selecionei o projeto firebase no qual definir a variável de configuração, para selecionar o projeto firebase use firebase use --add , a CLI irá avisá-lo com o lista de projetos em seu Firebase console (supondo que você esteja conectado à CLI com firebase login )

@dnhyde

funções do firebase

Parece que isso não funcionará ao usar em seu arquivo json qualquer outro tipo de valor, exceto strings (por exemplo, booleano ou número):

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

falha com:

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

Parece-me razoável que eu seja capaz de fazer:

firebase functions:config:get > config.json

e depois:

firebase functions:config:set < config.json

Faz sentido ter esses dois comandos complementares.

Ter a configuração em um arquivo me permite controlar sua versão e ver como ela mudou ao longo do tempo.

É uma pena que a única maneira que temos de fazer isso agora (usando env=$(cat config.json) ) também resulta em quebrar minha capacidade de ter config.json como os valores reais, já que não posso envolvê-los em { env: ... } .

Observação para mim: é aqui que preciso começar para uma solicitação de pull de recurso: https://github.com/firebase/firebase-tools/blob/b17611a4ff0d36e157ed06a24f6c81d4e146d9e2/src/functionsConfig.js#L142

Apenas compartilhando minha solução JS vanilla que funciona em relação à plataforma em que você desenvolve (Windows, MacOS, Linux):

Usando projetos separados para dev / test / prod.

/.firebaserc

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

Arquivos de configuração

Arquivos JSON na pasta /functions/config :

/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",
    ...
},
...

Apenas compartilhando minha solução JS vanilla que funciona em relação à plataforma em que você desenvolve (Windows, MacOS, Linux):

Usando projetos separados para dev / test / prod.

/.firebaserc

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

Arquivos de configuração

Arquivos JSON na pasta /functions/config :

/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",
    ...
},
...

Desculpe, mas quando você executa o script " config: set : dev"? Eu não entendo ... obrigado!

Esta página foi útil?
0 / 5 - 0 avaliações