Files
firebase-tools/scripts/test-functions-env.js
Daniel Lee d9aaaec1f3 Deploy environment variables in dotenv files for functions (#3645)
Add support for setting up environment variables for functions using dotenv files.

On deploy, Firebase CLI looks for naive dotenv file (`.env`) file or project-specific ones (`.env.<project id>` or `.env.<project alias>`) files, loads environment variables, and injects them on deployed functions. Environment variable set in project-specific dotenv file wins, and you cannot have both `.<project id>` and `<.project alias>` files.

Environment variables are also loaded up on Firebase Emulators. Emulator will look for `.env` and `.env.local` files and ignore project-specific dotenv files.

We are hiding the feature behind `dotenv` preview flag. We want to get a feeling for the development experience from our users before an official launch + documentations.

Also including a small e2e test for env var support. It's meant to run manually by the Firebase team via command `node scripts/test-functions-env.js`.
2021-08-11 13:31:18 -07:00

110 lines
3.3 KiB
JavaScript
Executable File

#!/usr/bin/env node
"use strict";
/**
* Integration test for functions env var support. Run:
* node ./test-functions-env.js <projectId> <region>
*
* If parameter ommited:
* - projectId defaults to `functions-integration-test`
* - region defaults to `us-central1`
*/
const expect = require("chai").expect;
const execSync = require("child_process").execSync;
const fs = require("fs-extra");
const path = require("path");
const tmp = require("tmp");
const api = require("../lib/api");
const cloudfunctions = require("../lib/gcp/cloudfunctions");
const { configstore } = require("../lib/configstore");
const scopes = require("../lib/scopes");
const source = __dirname + "/assets/functions_to_test_minimal.js";
const functionTarget = "httpsAction";
const projectId = process.argv[2] || "functions-integration-test";
const region = process.argv[3] || "us-central1";
const localFirebase = __dirname + "/../lib/bin/firebase.js";
const projectDir = __dirname + "/test-project";
let tmpDir;
let functionsSource;
function preTest() {
const dir = tmp.dirSync({ prefix: "envtest_" });
tmpDir = dir.name;
functionsSource = tmpDir + "/functions";
fs.copySync(projectDir, tmpDir);
execSync("npm install", { cwd: functionsSource, stdio: "ignore", stderr: "ignore" });
api.setRefreshToken(configstore.get("tokens").refresh_token);
api.setScopes(scopes.CLOUD_PLATFORM);
fs.copySync(source, functionsSource + "/index.js");
execSync(`${localFirebase} --open-sesame dotenv`, { cwd: tmpDir });
console.log("Done pretest prep.");
}
function postTest() {
fs.remove(tmpDir);
execSync(`${localFirebase} functions:delete ${functionTarget} --project=${projectId} -f`);
console.log("Done post-test cleanup.");
}
async function expectEnvs(envs) {
const fns = await cloudfunctions.listFunctions(projectId, region);
const fn = fns.find((fn) => fn.name.includes(functionTarget));
const gotEnvs = fn.environmentVariables;
// Remove system-provided environment variables.
delete gotEnvs.GCLOUD_PROJECT;
delete gotEnvs.FIREBASE_CONFIG;
expect(gotEnvs).to.be.deep.equals(envs);
console.log("PASS");
}
async function deployAndCompare(expected) {
execSync(`${localFirebase} deploy --only functions --project=${projectId}`, { cwd: tmpDir });
await expectEnvs(expected);
}
async function runTest(description, envFiles, expected) {
console.log("============================");
console.log(`Running test: ${description}`);
const toCleanup = [];
for (const [targetFile, data] of Object.entries(envFiles)) {
const fullPath = path.join(functionsSource, targetFile);
fs.writeFileSync(fullPath, data);
toCleanup.push(fullPath);
}
try {
await deployAndCompare(expected);
} finally {
for (const f of toCleanup) {
fs.unlinkSync(f);
}
}
}
(async () => {
try {
preTest();
await runTest(
"Inject environment variables from .env",
{ ".env": "FOO=foo\nBAR=bar\nCAR=car" },
{ FOO: "foo", BAR: "bar", CAR: "car" }
);
await runTest(
"Inject environment variables from .env and .env.<project>",
{ ".env": "FOO=foo\nSOURCE=env", [`.env.${projectId}`]: "SOURCE=env-project" },
{ FOO: "foo", SOURCE: "env-project" }
);
console.log("Success");
} catch (err) {
console.log("Error: " + err);
} finally {
postTest();
}
})();