Firebase-tools: Acepte el archivo JSON para las funciones: config: set

Creado en 20 jul. 2017  ·  37Comentarios  ·  Fuente: firebase/firebase-tools

Actualmente, es posible obtener la configuración completa de las funciones como un archivo JSON usando firebase functions:config:get , pero tenemos que alinear manualmente cada variable cuando usamos el comando firebase functions:config:set , lo que la hace prácticamente inutilizable cuando se trabaja en diferentes ambientes.

Idealmente, deberíamos poder hacer:

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

Comentario más útil

¡Gracias por compartir! @laurenzlong
Quería importar desde un archivo, así que usé un comando como este.

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

Importé un archivo json de la cuenta de servicio a Firebase Cloud Functions porque quería usar la API de administración de usuarios del SDK de Firebase Admin.

Todos 37 comentarios

Estoy de acuerdo con @jpreynat , config: establecido en nuestros scripts de implementación personalizados con un config.json sería una buena característica para ver agregada.
¿Hay alguna solución alternativa en este momento, tal vez una canalización en la configuración?

Parece que estás intentando replicar la configuración en todos los proyectos. ¿ firebase functions:config:clone funcionaría para usted?

Para dar un poco de contexto, nos hemos alejado intencionalmente de la configuración basada en archivos porque creemos que fomenta las malas prácticas (es decir, mantener un archivo lleno de secretos almacenados con su código, potencialmente incluso registrados en su repositorio fuente).

¿Qué pensaría de alguna manera (mecánica por determinar) para declarar variables de configuración "requeridas"? Y si intentara implementar sin la configuración requerida en el proyecto actual, se produciría un error antes de que se cambiara algo. ¿Resolvería eso sus principales preocupaciones? Si no es así, ¿por qué le resulta especialmente útil tener un archivo?

@mbleigh Eso es exactamente lo que no sabía que quería. ¡Buena idea!
Probablemente un dado / lo que quiso decir con 'Mechanics TBD', pero: Definir la estructura de claves / configuración en algún archivo dentro de nuestro proyecto en lugar de hacerlo de forma remota, para que pase por el proceso de revisión de git / PR como todo lo demás.
@jpreynat Lo siento por el problema de secuestro

@laurenzlong Gracias por el consejo, pero no estamos intentando duplicar una configuración.
Nuestra principal preocupación es actualizar la configuración de los diferentes proyectos en función de nuestra etapa de implementación.
Básicamente, tenemos un archivo de configuración por entorno (dev, staging y prod), y configuramos la configuración de Firebase respectivamente (para PR, presione el maestro y las etiquetas).

@mbleigh Gracias por la información.
Sin embargo, no estamos exactamente seguros de cómo podemos almacenar todos estos envs de otra manera que no sea usando archivos de configuración.
Actualmente están formateados como JSON para simplificar la lectura, pero estamos pensando en formatearlos como pares clave / valor para pasarlos directamente al comando functions:config:set .
Estoy de acuerdo con usted en que esto no es más seguro que pasar un archivo JSON.
¿Tiene alguna recomendación sobre cómo podríamos almacenar nuestras variables de entorno para diferentes etapas además del uso de archivos?

@ahaverty No te preocupes, me alegro de que esto se pueda debatir aquí.

@jpreynat cuando ejecuta functions:config:set se almacena con el proyecto actual. Si está usando firebase use para cambiar entre proyectos, su configuración será persistente. Entonces puede ejecutar set una vez por proyecto y luego siempre estará allí. Esto debería resolver el problema de la "configuración diferente para diferentes entornos", así que tal vez no entiendo el problema real.

@jpreynat Voy a cerrar este problema por ahora, pero vuelva a abrirlo si Michael y yo hemos entendido mal su solicitud.

@laurenzlong @mbleigh Lamento no haber respondido rápidamente en este hilo.
Encontramos una solución para este problema por ahora, pero aquí hay algo de contexto para aclarar mi solicitud:

Estamos implementando nuestra aplicación usando Travis (tenga en cuenta que esto sería genial con cualquier CI o incluso localmente). Dado que implementamos nuestra aplicación en diferentes etapas según la rama y la etiqueta, sería genial poder usar un archivo JSON diferente según el entorno.
Por ejemplo, esto permitiría hacer firebase config:set -f staging.json o firebase config:set -f prod.json dependiendo de esta condición.
Nuestra solución fue usar firebase como módulo de nodo, pero aún tenemos que incorporar nuestra configuración JSON, que es propensa a errores.

Hola @jpreynat , todavía estoy un poco confundido por tu respuesta. Una vez que estableces la configuración una vez, siempre permanece ahí, incluso en las implementaciones, a menos que desarmes explícitamente los valores de configuración ejecutando firebase functions: config : unset. Por tanto, no es necesario que esto forme parte del proceso de CI.

¿Alguien puede volver a abrir esto? %) @jpreynat @mbleigh Esa es realmente una de las cosas que falta. Atm Tengo cerca de 15 variables de configuración (y más por venir) y eso será muy útil si functions:config:set puede aceptar JSON de alguna manera.

Gran voto de nuestra parte por la sugerencia de @mbleigh sobre "declarar" variables de configuración requeridas ", y si intentaba implementar sin la configuración requerida en el proyecto actual, se produciría un error antes de que se cambiara algo".
Convertirse en un punto de dolor para nosotros también, pero eso resolvería todos nuestros problemas y encajaría muy bien en el proceso de control / revisión de versiones 👍

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

Hola @yaronyosef Puede proporcionar JSON directamente a las funciones de la base de fuego config : set, es solo que debe formatearlo de una manera que sea apropiada para su plataforma. Para Linux, lo siguiente debería funcionar:

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

¡Gracias por compartir! @laurenzlong
Quería importar desde un archivo, así que usé un comando como este.

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

Importé un archivo json de la cuenta de servicio a Firebase Cloud Functions porque quería usar la API de administración de usuarios del SDK de Firebase Admin.

¡Genial caso de uso! ¡Gracias por compartir!

También preferiríamos rastrear configuraciones no secretas a través de Git, donde los archivos de configuración de tiempo de ejecución JSON comprometidos (para múltiples entornos) serán autorizados y el entorno de CI luego aplicaría functions:config:set del archivo correspondiente a un entorno particular (y cada entorno corresponde a un proyecto de Firebase independiente).

Si mantiene las configuraciones en git, es trivial leerlas en el tiempo de ejecución de sus funciones. Solo póngalos en configs/<project_id>.json y luego:

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

La configuración de tiempo de ejecución utilizada por Firebase está diseñada específicamente para evitar que la configuración se registre en git, ya que (especialmente con los secretos) hemos descubierto que es un patrón que causa más problemas de los que resuelve.

Eso es cierto, aunque ya no usamos la API de configuración para este propósito. Puede ser beneficioso seguir pasando por la API de configuración para almacenar valores confidenciales (que parece ser un caso de uso apropiado, según los documentos de Firebase) y cargar valores controlados por fuente y sin seguimiento de la misma manera exacta (es decir, a través de config() proporcionado por firebase-functions ).

Por el momento, estoy usando el siguiente resumen para alimentar el .runtimeconfig completo a functions:config:set :

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

donde FIREBASE_ENV es el nombre del entorno utilizado en la implementación (por ejemplo, dev ). Sería bueno poder acortar esto aún más, p. Ej.

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

Sin embargo, en última instancia, incluso eso puede no ser necesario si la API de configuración habilitó pistas de auditoría fáciles de usar de todos los cambios (corríjame si ese ya es el caso, me gustaría explorar eso).

Entonces, ¿cómo va esto? 😄

¡Me encantaría ver la sugerencia de @mbleigh aún implementada! Constantemente tenemos problemas para olvidar establecer configuraciones en nuestros objetivos que no son de desarrollo.

Gracias por el golpe de hilo. Aún no hay nada que informar, pero volveré a mencionar este tema en nuestras reuniones de planificación para ver si podemos comenzar a hacer algún progreso al respecto.

Valido mi configuración con un archivo de esquema JSON para asegurarme de que tenga todos los valores que espero. Esta validación es parte de mi canalización de CI y evita la implementación si falla. Si, como yo, usa TypeScript para escribir sus funciones, puede generar el esquema JSON a partir de sus tipos.

Funciona muy bien, con la limitación de que la configuración de funciones solo acepta cadenas, por lo que el esquema no puede validar que un valor de configuración sea un número. Para los valores booleanos, está bien porque puedo usar una enumeración con "true" y "false" como los únicos valores válidos.

Consulte mi configuración de CircleCI y esta publicación de blog como referencia.

@hgwood ¡ gracias por compartir! Definitivamente también agregaremos esto a nuestro IC.

Solo quería dejar mi solución a este "problema" y mis pensamientos sobre la discusión:

  1. Entiendo que los valores secretos / sensibles NO deben almacenarse en el repositorio. Mi solución es solo para valores no secretos. Los valores secretos se establecen manualmente, pero una forma de marcar los valores de configuración según sea necesario sería maravillosa para esos valores secretos.
  2. Creo que esto es más importante de lo que algunos parecen pensar. Es un PITA tener que establecer manualmente valores de configuración individuales para diferentes proyectos de entorno de implementación y no poder verlos / compararlos simultáneamente fácilmente. La definición de configuraciones en archivos hace que el proceso sea MUCHO menos complicado.
  3. Creo que agregar un editor de configuración de funciones a la consola web mejoraría mucho esto, pero sería realmente bueno si hubiera una forma de ver simultáneamente configuraciones para múltiples proyectos, de modo que pueda comparar fácilmente configuraciones para diferentes entornos de implementación.
  4. Siempre he dicho que firebase debe permitir que los entornos se definan dentro de un solo proyecto, por lo que no necesita varios proyectos para usar como entornos de implementación. Si esto existiera, sería muy fácil ver simultáneamente configuraciones para múltiples entornos dentro de un solo proyecto.
  5. Mi solución probablemente tiene problemas, no confíe en mi código literalmente, pero me gusta el concepto general.

Mi solución

Utilizo archivos de configuración yaml en cascada, que son útiles para los valores predeterminados y los valores que se aplican a todas las configuraciones, independientemente del entorno de implementación.

./config/default.yml

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

./config/dev.yml (archivos de configuración similares para otros entornos de implementación)

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

Luego, escribí un pequeño script de nodo que carga la configuración resuelta y devuelve una lista delimitada por espacios de pares clave = 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 la implementación de CI, utilizo este script de shell bash para obtener los pares key = val y configurarlos con funciones: 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 Me gusta su solución cat , pero ¿no encontró un error Precondition check failed al intentar cargar su archivo cert?

Terminamos usando Deployment Manager por estos motivos.

Establecemos toda la configuración a través de "config Yaml" para DM. Luego, DM crea las configuraciones y variables de tiempo de ejecución apropiadas, y firebase deploy recupera y establece sus valores en el momento de la implementación sin tener que usar firebase config:set en absoluto. Esto también nos permite configurar otros recursos (por ejemplo, depósitos y su control de acceso) y establecer sus valores en RC sin tener que suministrarlos externamente a la plantilla. Además, esto nos permite usar camelCase config y nombres de variables.

La única vez que establecemos un valor externamente es cuando necesitamos pasar una clave de cuenta de servicio (encriptada) a través de la configuración de tiempo de ejecución, porque DM no puede encriptar valores de forma segura. Pero todavía usamos gcloud beta runtime-config configs variables set para eso (después de la implementación de DM y antes de firebase deploy ) y no firebase config:set , porque el primero acepta stdout de KMS.

Solo quería agregar: al leer esto, parece que algunas personas pasaron por alto un punto crucial que conduce a cierta confusión. Sería realmente útil si esto también se aclarara en la documentación.

Básicamente, functions.config() persiste entre _ implementaciones_, ¡pero TAMBIÉN se puede configurar por separado entre diferentes proyectos de base de fuego!

Esto significa que para los entornos de desarrollo, preparación y producción, debe configurar varios proyectos de base de fuego idénticos (en lugar de utilizar alojamiento de varios sitios o implementar en el MISMO proyecto ... 👎), cada uno de los cuales tiene sus propias variables de entorno diferentes de forma persistente. .

Una vez que haya establecido los alias (o simplemente haya cambiado entre proyectos con firebase use ), puede implementar el mismo repositorio en varios proyectos de base de fuego separados. Así es como configuras un entorno de desarrollo, preparación y producción, como proyectos de base de fuego separados.

Luego, puede establecer configuraciones de entorno separadas para cada uno, como este:
firebase -P dev config:set - establece variables para tu entorno de desarrollo
firebase -P prod config:set - establece variables para tu entorno de producción ...

Luego, puede implementar el mismo repositorio dos veces, así:
firebase deploy -P dev
firebase deploy -P prod
y ambos se implementarán, en su respectivo proyecto de base de fuego, y contendrán su propia configuración de entorno separada que persistirá entre implementaciones.

Sí, así es como lo hacemos nosotros también. Proyectos separados para dev / test / prod.

Solo comparto mi solución vanilla JS que funciona con respecto a la plataforma en la que se desarrolla (Windows, MacOS, Linux):

Usando proyectos separados para dev / test / prod.

/.firebaserc

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

Archivos de configuración

Archivos JSON en la carpeta /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",
    ...
},
...

Si alguien todavía está mirando, existe el método utilizado en este artículo mediano https://medium.com/indielayer/deploying-environment-variables-with-firebase-cloud-functions-680779413484. Salvó mi vida.

Lo que personalmente me falta es la capacidad de establecer una variable de configuración sin realizar una implementación. Tenemos una canalización CICD que se implementa en nuestros diferentes entornos y no queremos que un desarrollador compile el código localmente y lo implemente, especialmente no en producción. Así es como se rompen las cosas.

Además, si queremos cambiar una variable existente más adelante, con alguna característica en desarrollo, ahora mismo debe verificar la última etiqueta lanzada, compilarla, implementarla, regresar a su rama de características, continuar. Eso realmente hace que esto sea inutilizable.

Esencialmente, querríamos una configuración remota para las funciones en la nube.

@bezysoftware Creo que necesita usar variables de entorno (en lugar de la configuración del entorno), puede omitir la capa de base de fuego y hacerlo en la plataforma en la nube de Google, compruébelo https://cloud.google.com/functions/docs/env- var

¡Gracias por compartir! @laurenzlong
Quería importar desde un archivo, así que usé un comando como este.

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

Importé un archivo json de la cuenta de servicio a Firebase Cloud Functions porque quería usar la API de administración de usuarios del SDK de Firebase Admin.

No funciono

@ Md-Abdul-Halim-Rafi

funciones de firebase

Funcionó para mí, puedo ver la var de configuración con firebase functions:config:get .
No funcionó la primera vez porque no estaba en la carpeta del proyecto y no seleccioné el proyecto de base de fuego en el que establecer la variable de configuración, para seleccionar el proyecto de base de fuego use firebase use --add , la CLI le indicará el lista de proyectos en su consola de base de fuego (suponiendo que haya iniciado sesión en la CLI con firebase login )

@dnhyde

funciones de firebase

Parece que esto no funcionará mientras usa en su archivo json cualquier otro tipo de valor, excepto cadenas (por ejemplo, booleano o número):

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

falla con:

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

Me parece razonable que pueda hacer:

firebase functions:config:get > config.json

y luego más tarde:

firebase functions:config:set < config.json

Simplemente tiene sentido que estos dos comandos sean complementarios.

Tener la configuración en un archivo me permite controlar la versión y ver cómo ha cambiado con el tiempo.

Es lamentable que la única forma que tenemos de lograr esto en este momento (usando env=$(cat config.json) ) también resulte en romper mi capacidad para que config.json sean los valores reales ya que no puedo envolverlos en { env: ... } .

Nota para mí: aquí es donde necesito comenzar para una solicitud de extracción de funciones: https://github.com/firebase/firebase-tools/blob/b17611a4ff0d36e157ed06a24f6c81d4e146d9e2/src/functionsConfig.js#L142

Solo comparto mi solución vanilla JS que funciona con respecto a la plataforma en la que se desarrolla (Windows, MacOS, Linux):

Usando proyectos separados para dev / test / prod.

/.firebaserc

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

Archivos de configuración

Archivos JSON en la carpeta /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",
    ...
},
...

Solo comparto mi solución vanilla JS que funciona con respecto a la plataforma en la que se desarrolla (Windows, MacOS, Linux):

Usando proyectos separados para dev / test / prod.

/.firebaserc

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

Archivos de configuración

Archivos JSON en la carpeta /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",
    ...
},
...

Lo siento, pero ¿cuándo ejecuta el script " config: set : dev"? No lo entiendo ... ¡gracias!

¿Fue útil esta página
0 / 5 - 0 calificaciones