Dynamic firebase.json file - firebase

It is quite difficult to keep track of the CSP rules in the firebase.json file, therefore I decided to add a script that constructs the proper rules. Now I need to manually update the firebase.json file every time I am changing something in the CSP configuration. Is there any way to dynamically configure firebase? I was hoping to achieve it with a simple renaming as it worked for many services grunt.json -> grunt.js I guess I can use some sort of templating but I wonder if there's a built-in mode to dynamically construct headers, rules etc for firebase.

There isn't a built-in way to generate firebase.json on command. I would recommend creating a script, e.g. npm run deploy that first generates your firebase.json content and then runs firebase deploy. That way you can make sure it's always regenerated before deployment.
// configureFirebase.js
const fs = require('fs');
const config = {
hosting: {
// ...
}
}
fs.writeFileSync(__dirname + '/firebase.json', JSON.stringify(config));
// package.json
{
"scripts": {
"deploy": "node configureFirebase.js && firebase deploy"
}
}

Related

How do I deploy Cloud Functions while ignoring existing functions?

Say I have the following four functions in my Firebase projects:
openDoor(europe-west1)
closeDoor(europe-west1)
openWindow(europe-west1)
closeWindow(europe-west1)
Now, these functions live in two separate Node packages, i.e. one that contains openDoor and closeDoor and another one that contains openWindow and closeWindow.
Error
If I try to run firebase deploy from the package with the door functions, the following error will be thrown (in non-interactive mode):
Error: The following functions are found in your project but do not exist in your local source code:
openWindow(europe-west1)
closeWindow(europe-west1)
This is a problem because it will cancel any CD workflow that tries to deploy these functions.
Force delete
There is an option to force-delete any existing functions:
-f, --force delete Cloud Functions missing from the current
working directory without confirmation
However, I want the opposite. I want to keep all existing functions.
Theoretical workaround
There is one workaround that I found would work in theory, which is:
yes N | firebase deploy --interactive
Piping N into the interactive deploy command, which will answer N to the deletion prompt:
The following functions are found in your project but do not exist in your local source code:
openWindow(europe-west1)
closeWindow(europe-west1)
If you are renaming a function or changing its region, it is recommended that you create the new function first before deleting the old one to prevent event loss. For more info, visit https://firebase.google.com/docs/functions/manage-functions#modify
? Would you like to proceed with deletion? Selecting no will continue the rest of the deployments. (y/N)
The problem now is that I am using https://github.com/w9jds/firebase-action to deploy the functions, which means that I need to have a built-in Firebase solution.
You can make use of the new codebases feature in Firebase.
By specifying a codebase in your firebase.json functions configuration, this problem is solved. The Firebase CLI will no longer prompt you to delete other functions as it only considers the functions of the same codebase.
If your firebase.json previously looked like this:
{
"functions": {
"source": "cloud_functions",
"ignore": [...],
"predeploy": [...],
"postdeploy": [...]
}
}
You only need to add "codebase": "<name>" to the config:
{
"functions": {
"source": "cloud_functions",
"codebase": "window",
"ignore": [...],
"predeploy": [...],
"postdeploy": [...]
}
}
The deploy will now look like this:
i functions: updating Node.js 16 function window:openWindow(europe-west1)...
i functions: updating Node.js 16 function window:closeWindow(europe-west1)...
Note that the actual function name does not change, i.e. the function will still only be called openWindow (without the prefix) in the Firebase / Google Cloud Console. So this is basically the perfect solution to the problem.
Alternatively, you can also specify the function names when performing deployment.
firebase deploy --only functions:openDoor,functions:closeDoor

Firebase Hosting: Function not working with ServerMiddleware (Vue/ Nuxt)

I am building a project that utilises ServerMiddleware to render some pages client side only (I can't find another way of getting this working well without ServerMiddleware. Problems on refreshing pages and so on...)
The problem: Unfortunately every time I try and deploy to my Firebase Function through 'firebase deploy' I get an error:
Error: Cannot find module '~/serverMiddleware/selectiveSSR.js'
The function builds OK if I exclude the following line. Nuxt/ Vue is not including ~/serverMiddleware/ as part of its build as far as I can see.
Here is the code in nuxt.config.js to reference my serverMiddleware:
serverMiddleware: ['~/serverMiddleware/selectiveSSR.js']
Adding either the directory or path (as above) to the file itself within Build in nuxt.config.js does not help either. Maybe I am doing it wrong?
Everything works perfectly when testing (Not building) locally.
Any ideas on how I can resolve this please?
Thanks!
Ok so for anyone else who hits this, here is how I got around it.
Firstly, I don't know if this is the fault of Firebase Hosting or Nuxt (I would guess Nuxt but I stand to be corrected), but here is what to do....
1) Remove any reference to ServerMiddleware from nuxt.config.js
2) Add the following to nuxt.config.js
modules: [
'~/local-modules/your-module-name'
],
3) Create directory ~/local-modules/your-module-name in your project root
4) In the new directory, create a package.json:
{
"name": "your-module-name",
"version": "1.0.0"
}
and index.js - key thing, this.addServerMiddleware allows you to call middleware server-side
module.exports = function(moduleOptions) {
this.addServerMiddleware('~/serverMiddleware/')
}
5) Create directory ~/serverMiddleware
6) Add your middleware function to index.js in the new directory:
export default function(req, res, next) {
// YOUR CODE
next() // Always end with next()!
}
7) Update package.json with your new local module under "dependencies":
"your-module-name": "file:./local-modules/your-module-name"
Don't forget you need to do this within the functions directory too or Firebase will complain it can't find your new module

Where to put secret keys in Netlify? [duplicate]

I'm trying to set an environment variable for an API key that I don't want in my code. My source javascript looks something like this :
.get(`http://api-url-and-parameters&api-key=${process.env.API_KEY}`)
I'm using webpack and the package dotenv-webpack https://www.npmjs.com/package/dotenv-webpack to set API_KEY in a gitignored .env file and it's all running fine on my local. I'd like to also be able to set that variable when deploying through Netlify, I've tried adding it through to GUI to the 'build environment variables', and also to set it directly in the build command, but without success.
Any idea what might be the issue ?
WARNING: If this is a secret key, you will not want to expose this environment variable value in any bundle that gets returned to the client. It should only be used by your build scripts to be used to create your content during build.
Issue
dotenv-webpack expects there to be a .env file to load in your variables during the webpack build of your bundle. When the repository is checked out by Netlify, the .env does not exist because for good reason it is in .gitignore.
Solution
Store your API_KEY in the Netlify build environment variables and build the .env using a script prior to running the build command.
scripts/create-env.js
const fs = require('fs')
fs.writeFileSync('./.env', `API_KEY=${process.env.API_KEY}\n`)
Run the script as part of your build
node ./scripts/create-env.js && <your_existing_webpack_build_command>
Caveats & Recommendations
Do not use this method with a public facing repository [open] because any PR or branch deploy could create a simple script into your code to expose the API_KEY
The example script above is for simplicity so, make any script you use be able to error out with a code other than 0 so if the script fails the deploy will fail.
You can set Dotenv-webpack to load system environment variables as well as those you have declared in your .env file by doing the following:
plugins: [
new Dotenv({
systemvars: true
})
]
I.e Setting the systemvars attribute of your webpack dotenv plugin to true.
Note that system environment variables with the same name will overwrite those defined in your .env file.
Source: https://www.npmjs.com/package/dotenv-webpack#properties
if you go to corresponding site's settings in Netlify, under build&deploy you can find a section called environment variables you can easily add your environment variables from there. if you add MY_API_KEY variable to environment variables you will be able to access it inside your project via process.env.MY_API_KEY.
If you're using Nuxt JS there is a more "straight forward" approach.
Just edit the nuxt.config.js like so:
module.exports = {
env: {
GOOGLE_API_KEY: process.env.GOOGLE_API_KEY
},
// ...
Then add the GOOGLE_API_KEY to Netlify through the build environment variables as usual.
Credit goes to yann-linn and his answer on github.
What you can also do is also to define a global constant in Webpack. Netlify environment variables defined in UI will work with it. You don't need dotenv or dotenv-webpack.
webpack.config.js
const webpack = require("webpack");
module.exports = {
plugins: [
new webpack.DefinePlugin({
"process.env.API_KEY": JSON.stringify(process.env.API_KEY)
}),
]
}
However again, of course you shouldn't do it just inputting enviornmental variables in the frontend if your API key is confidential and project public. The API key will appear in the source code of the website and will be easily accessible for everyone visiting it. Lambda function would be a better option.
You can use the Netlify's config file also ...
You can find documentation here.
Also i wanted to have the same ENV variables with with different values per branch/environment.
This workaround worked for me:
Create a netlify.toml file like:
[build]
NUXT_ENV_BASE_API = "/api"
NUXT_ENV_HOST_DOMAIN = "https://your-domain.gr"
[context.branch-deploy]
environment = { NUXT_ENV_BASE_API = "/dev-api", NUXT_ENV_HOST_DOMAIN = "https://dev.your-domain.gr" }
[context.production]
environment = { NUXT_ENV_BASE_API = "/api", NUXT_ENV_HOST_DOMAIN = "https://your-domain.gr" }
And deploy in Netlify ...

Firebase cannot understand what targets to deploy

When deploying the following hello-world equivalent code I get the error shown in the end:-
$ ls -lR
.:
total 8
-rw-r--r-- 1 hgarg hgarg 3 Aug 29 14:55 firebase.json
drwxr-xr-x 2 hgarg hgarg 4096 Aug 29 11:56 functions
./functions:
total 4
-rw-r--r-- 1 hgarg hgarg 1678 Aug 29 11:56 index.js
firebase.json looks like this:-
{}
and index.json like this:-
'use strict';
const functions = require('firebase-functions');
exports.search = functions.https.onRequest((req, res) => {
if (req.method === 'PUT') {
res.status(403).send('Forbidden!');
}
var category = 'Category';
console.log('Sending category', category);
res.status(200).send(category);
});
But deploying fails:-
$ firebase deploy
Error: Cannot understand what targets to deploy. Check that you specified valid targets if you used the --only or --except flag. Otherwise, check your firebase.json to ensure that your project is initialized for the desired features.
$ firebase deploy --only functions
Error: Cannot understand what targets to deploy. Check that you specified valid targets if you used the --only or --except flag. Otherwise, check your firebase.json to ensure that your project is initialized for the desired features.
it would be better to pre-populate the firebase with the default options. I choose that I wanted to use only hosting the firebase.json should have be created with the default hosting option.
{
"hosting": {
"public": "public"
}
}
or you try run firebase init again.
Faced similar issue. In firebase.json file (in hosting parameter), we have to give the name of directory that we want to deploy (in my case, I was already in the directory, which I wanted to deploy, hence I put "." in hosting specification). It solved the error for me.
{
"hosting": {
"public": ".",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
}
}
I was facing this issue when running:
firebase emulators:exec --only firestore 'npm tst'
The problem was that on firebase.json must have a property for the emulator you want. So in my case I added a "firestore": {} on firebase.json and worked.
I faced this problem too because at the beginning of my Firebase project setup, I only initialized the hosting feature. Later on, when I wanted to deploy firestore security rules with the Firebase CLI, my initialization process was not complete for this command to work as expected.
I could not run firebase deploy --only firestore:rules
because my firebase.json file was not initialized with defaults.
Easiest and most adequate way to fix this problem is to run the firebase init command again to setup all features you want to use. You could do it manually but you could miss details that the command line interface can setup for you in the exact way it needs to be for defaults.
Solution:
Run the firebase init command again
...and make sure to initialize every feature you are currently using. Take care not to overwrite important configs if you already have some by carefully reading the Firebase CLI instructions that are asked by the init command.
Firebase reads package.json to read details of the functions target. This file was missing from my project directory, as I had moved files around after doing an init.
Creating a clean directory and doing a firebase init functions inside it created all the required files and folders to get started.
I think you are missing on one of the following things -
a) you should run firebase init outside of the main project where index.html is.
b) select hosting option after running firebase init by pressing SPACE
c) Please give folder name which contain index.html in it.
And your project will be up running.

Firebase configuration for multiple projects/environments

I'm using Cloud Functions for Firebase with three different projects for development, testing and production purposes. Each project has a service-account.json. When I deploy the sources to an environment, the initialization looks like this:
var serviceAccount = require("./service-account-dev.json");
firebase.initializeApp({
credential: firebase.credential.cert(serviceAccount),
databaseURL: "https://nwDEV.firebaseio.com"
});
This is a bit difficult to handle, because I have to change the code everytime I want to deploy to a different environment. Is there a way to have an overall configuration, e.g. in firebase.json or.firebasesrc, which allows to integrate the service-account and decides on deployment which configuration to choose?
Otherwise is there a possibility to detect under which environment the code is running and to load the specific service-account.json and to set the databaseURL-property?
You can use environment variables. https://firebase.google.com/docs/functions/config-env
Select the project (you can use the command firebase projects:list to see them):
firebase use my-project-development
Set an environment variable
firebase functions:config:set app.environment="dev"
In your functions file, apply a conditional to choose the file:
const serviceAccount = functions.config().app.environment === 'dev' ? 'credentials-dev.json' : 'credentials-prod.json';
Then you can use the file depending on the project:
firebase.initializeApp({
credential: firebase.credential.cert(serviceAccount),
databaseURL: "https://nwDEV.firebaseio.com"
});
From what I understand of your question, what you are looking for boils down to a solution to translate the cloud functions you are deploying to the appropriate settings, i.e. production, development, and testing, which I assume means each of these is a unique project, and therefore database, in your Firebase environment.
If the above is true then the following should help.
Firebase Cloud Functions, and CLI more generally, is able to deploy to a specific project in your Firebase environment. To do this execute the following command in the terminal while in the cloud functions directory.
$ firebase use --add
This will allow you to pick your additional project (for instance, development) and assign it an alias (I recommend "development" if it is as such). Then when deploying your functions you can choose which project (and therefore database) to deploy to by using the alias.
$ firebase use default # sets environment to the default alias
$ firebase use development # sets environment to the development alias
For more information please see: https://firebase.googleblog.com/2016/07/deploy-to-multiple-environments-with.html
One thing you may have to do for this to work would be to use the default config settings for Cloud Functions.
$ admin.initializeApp(functions.config().firebase);
Short answer:
the GCLOUD_PROJECT environment variable will be unique to your project, hence you can utilise it like this (sample code is for 2 different projects but you can extend it using switch or any other conditional statement):
const env = process.env.GCLOUD_PROJECT === 'my-app-prod' ? 'prod' : 'dev';
then use that env variable to load intended configuration.
Full example: (TypeScript)
update .firebaserc file
{
"projects": {
"default": "my-app-dev",
"prod": "my-app-prod",
}
}
create and modify your ./somewhere/config.ts file accordingly, let's say you're using AWS services (please ensure to secure your configuration details)
export const config = {
dev: {
awsRegion: 'myDevRegion',
awsAccessKey: 'myDevKey',
awsSecretKey: 'myDevSecretKey'
},
prod: {
awsRegion: 'myProdRegion',
awsAccessKey: 'myProdKey',
awsSecretKey: 'myProdSecretKey'
}
};
now above items can be used in the index.ts file
import { config } from './somewhere/config';
import * as aws from 'aws-sdk';
. . .
const env = process.env.GCLOUD_PROJECT === 'my-app-prod' ? 'prod' : 'dev';
const awsCredentials = {
region: config[env].awsRegion,
accessKeyId: config[env].awsAccessKey,
secretAccessKey: config[env].awsSecretKey
};
aws.config.update(awsCredentials);
. . .
export const myFuncToUseAWS = functions....
Now the deployment
Dev environment deployment: $ firebase deploy --only functions -P default
Prod environment deployment: $ firebase deploy --only functions -P prod

Resources