firebase-tools 10.1.3 → 10.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api.js +1 -0
- package/lib/apiv2.js +4 -0
- package/lib/auth.js +62 -25
- package/lib/commands/auth-import.js +1 -1
- package/lib/commands/ext-configure.js +1 -0
- package/lib/commands/ext-install.js +1 -0
- package/lib/commands/ext-uninstall.js +1 -0
- package/lib/commands/ext-update.js +1 -0
- package/lib/commands/functions-config-clone.js +1 -1
- package/lib/commands/functions-secrets-access.js +17 -0
- package/lib/commands/functions-secrets-destroy.js +40 -0
- package/lib/commands/functions-secrets-get.js +21 -0
- package/lib/commands/functions-secrets-prune.js +50 -0
- package/lib/commands/functions-secrets-set.js +46 -0
- package/lib/commands/index.js +7 -3
- package/lib/commands/login.js +1 -1
- package/lib/deploy/functions/backend.js +9 -1
- package/lib/deploy/functions/ensure.js +112 -0
- package/lib/deploy/functions/ensureCloudBuildEnabled.js +0 -49
- package/lib/deploy/functions/prepare.js +12 -18
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +1 -0
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +12 -0
- package/lib/deploy/functions/validate.js +56 -1
- package/lib/deploy/hosting/client.js +9 -0
- package/lib/deploy/hosting/convertConfig.js +6 -0
- package/lib/deploy/hosting/index.js +5 -5
- package/lib/deploy/hosting/prepare.js +25 -25
- package/lib/deploy/hosting/release.js +21 -24
- package/lib/emulator/commandUtils.js +5 -1
- package/lib/emulator/controller.js +3 -1
- package/lib/emulator/downloadableEmulators.js +29 -12
- package/lib/emulator/emulatorLogger.js +7 -0
- package/lib/emulator/functionsEmulator.js +122 -80
- package/lib/emulator/functionsEmulatorRuntime.js +100 -83
- package/lib/emulator/functionsEmulatorShared.js +51 -1
- package/lib/emulator/functionsEmulatorShell.js +1 -2
- package/lib/emulator/functionsRuntimeWorker.js +1 -1
- package/lib/emulator/storage/apis/gcloud.js +2 -2
- package/lib/emulator/storage/files.js +8 -3
- package/lib/extensions/askUserForParam.js +1 -1
- package/lib/extensions/diagnose.js +56 -0
- package/lib/extensions/extensionsHelper.js +10 -17
- package/lib/extensions/listExtensions.js +2 -0
- package/lib/extensions/resolveSource.js +1 -53
- package/lib/extensions/secretsUtils.js +1 -1
- package/lib/extensions/updateHelper.js +0 -14
- package/lib/extensions/utils.js +4 -2
- package/lib/functions/env.js +5 -7
- package/lib/functions/secrets.js +112 -0
- package/lib/gcp/cloudfunctions.js +2 -2
- package/lib/gcp/secretManager.js +128 -46
- package/lib/gcp/storage.js +5 -3
- package/lib/hosting/functionsProxy.js +15 -5
- package/lib/previews.js +1 -1
- package/lib/responseToError.js +16 -7
- package/lib/serve/functions.js +2 -2
- package/lib/serve/hosting.js +1 -1
- package/lib/utils.js +6 -1
- package/npm-shrinkwrap.json +124 -45
- package/package.json +4 -4
- package/schema/firebase-config.json +27 -0
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const fs = require("fs");
|
|
4
|
-
const types_1 = require("./types");
|
|
5
|
-
const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
|
|
6
|
-
const functionsEmulatorUtils_1 = require("./functionsEmulatorUtils");
|
|
7
4
|
const express = require("express");
|
|
8
5
|
const path = require("path");
|
|
9
6
|
const bodyParser = require("body-parser");
|
|
10
7
|
const url_1 = require("url");
|
|
11
8
|
const _ = require("lodash");
|
|
12
|
-
|
|
9
|
+
const types_1 = require("./types");
|
|
10
|
+
const constants_1 = require("./constants");
|
|
11
|
+
const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
|
|
12
|
+
const functionsEmulatorUtils_1 = require("./functionsEmulatorUtils");
|
|
13
|
+
let functionTrigger;
|
|
14
|
+
let FUNCTION_TARGET_NAME;
|
|
15
|
+
let FUNCTION_SIGNATURE;
|
|
13
16
|
let developerPkgJSON;
|
|
14
17
|
const dynamicImport = new Function("modulePath", "return import(modulePath)");
|
|
15
18
|
function isFeatureEnabled(frb, feature) {
|
|
@@ -112,7 +115,7 @@ async function resolveDeveloperNodeModule(frb, name) {
|
|
|
112
115
|
if (!isInPackageJSON) {
|
|
113
116
|
return { declared: false, installed: false };
|
|
114
117
|
}
|
|
115
|
-
const resolveResult = await requireResolveAsync(name, { paths: [
|
|
118
|
+
const resolveResult = await requireResolveAsync(name, { paths: [process.cwd()] }).catch(noOp);
|
|
116
119
|
if (!resolveResult) {
|
|
117
120
|
return { declared: true, installed: false };
|
|
118
121
|
}
|
|
@@ -160,7 +163,7 @@ function requirePackageJson(frb) {
|
|
|
160
163
|
return developerPkgJSON;
|
|
161
164
|
}
|
|
162
165
|
try {
|
|
163
|
-
const pkg = require(`${
|
|
166
|
+
const pkg = require(`${process.cwd()}/package.json`);
|
|
164
167
|
developerPkgJSON = {
|
|
165
168
|
engines: pkg.engines || {},
|
|
166
169
|
dependencies: pkg.dependencies || {},
|
|
@@ -321,7 +324,7 @@ function getDefaultConfig() {
|
|
|
321
324
|
}
|
|
322
325
|
function initializeRuntimeConfig(frb) {
|
|
323
326
|
if (!process.env.CLOUD_RUNTIME_CONFIG) {
|
|
324
|
-
const configPath = `${
|
|
327
|
+
const configPath = `${process.cwd()}/.runtimeconfig.json`;
|
|
325
328
|
try {
|
|
326
329
|
const configContent = fs.readFileSync(configPath, "utf8");
|
|
327
330
|
if (configContent) {
|
|
@@ -359,7 +362,7 @@ async function initializeFirebaseAdminStubs(frb) {
|
|
|
359
362
|
const defaultApp = makeProxiedFirebaseApp(frb, adminModuleTarget.initializeApp(defaultAppOptions));
|
|
360
363
|
logDebug("initializeApp(DEFAULT)", defaultAppOptions);
|
|
361
364
|
localFunctionsModule.app.setEmulatedAdminApp(defaultApp);
|
|
362
|
-
if (
|
|
365
|
+
if (process.env[constants_1.Constants.FIREBASE_AUTH_EMULATOR_HOST]) {
|
|
363
366
|
if ((0, functionsEmulatorUtils_1.compareVersionStrings)(adminResolution.version, "9.3.0") < 0) {
|
|
364
367
|
new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Firebase Authentication emulator is running, but your 'firebase-admin' dependency is below version 9.3.0, so calls to Firebase Authentication will affect production.").log();
|
|
365
368
|
}
|
|
@@ -377,16 +380,20 @@ async function initializeFirebaseAdminStubs(frb) {
|
|
|
377
380
|
return defaultApp;
|
|
378
381
|
})
|
|
379
382
|
.when("firestore", (target) => {
|
|
380
|
-
warnAboutFirestoreProd(
|
|
383
|
+
warnAboutFirestoreProd();
|
|
381
384
|
return Proxied.getOriginal(target, "firestore");
|
|
382
385
|
})
|
|
383
386
|
.when("database", (target) => {
|
|
384
|
-
warnAboutDatabaseProd(
|
|
387
|
+
warnAboutDatabaseProd();
|
|
385
388
|
return Proxied.getOriginal(target, "database");
|
|
386
389
|
})
|
|
387
390
|
.when("auth", (target) => {
|
|
388
|
-
warnAboutAuthProd(
|
|
391
|
+
warnAboutAuthProd();
|
|
389
392
|
return Proxied.getOriginal(target, "auth");
|
|
393
|
+
})
|
|
394
|
+
.when("storage", (target) => {
|
|
395
|
+
warnAboutStorageProd();
|
|
396
|
+
return Proxied.getOriginal(target, "storage");
|
|
390
397
|
})
|
|
391
398
|
.finalize();
|
|
392
399
|
require.cache[adminResolution.resolution] = Object.assign(require.cache[adminResolution.resolution], {
|
|
@@ -401,37 +408,47 @@ function makeProxiedFirebaseApp(frb, original) {
|
|
|
401
408
|
const appProxy = new Proxied(original);
|
|
402
409
|
return appProxy
|
|
403
410
|
.when("firestore", (target) => {
|
|
404
|
-
warnAboutFirestoreProd(
|
|
411
|
+
warnAboutFirestoreProd();
|
|
405
412
|
return Proxied.getOriginal(target, "firestore");
|
|
406
413
|
})
|
|
407
414
|
.when("database", (target) => {
|
|
408
|
-
warnAboutDatabaseProd(
|
|
415
|
+
warnAboutDatabaseProd();
|
|
409
416
|
return Proxied.getOriginal(target, "database");
|
|
410
417
|
})
|
|
411
418
|
.when("auth", (target) => {
|
|
412
|
-
warnAboutAuthProd(
|
|
419
|
+
warnAboutAuthProd();
|
|
413
420
|
return Proxied.getOriginal(target, "auth");
|
|
421
|
+
})
|
|
422
|
+
.when("storage", (target) => {
|
|
423
|
+
warnAboutStorageProd();
|
|
424
|
+
return Proxied.getOriginal(target, "storage");
|
|
414
425
|
})
|
|
415
426
|
.finalize();
|
|
416
427
|
}
|
|
417
|
-
function warnAboutFirestoreProd(
|
|
418
|
-
if (
|
|
428
|
+
function warnAboutFirestoreProd() {
|
|
429
|
+
if (process.env[constants_1.Constants.FIRESTORE_EMULATOR_HOST]) {
|
|
419
430
|
return;
|
|
420
431
|
}
|
|
421
432
|
new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Cloud Firestore emulator is not running, so calls to Firestore will affect production.").log();
|
|
422
433
|
}
|
|
423
|
-
function warnAboutDatabaseProd(
|
|
424
|
-
if (
|
|
434
|
+
function warnAboutDatabaseProd() {
|
|
435
|
+
if (process.env[constants_1.Constants.FIREBASE_DATABASE_EMULATOR_HOST]) {
|
|
425
436
|
return;
|
|
426
437
|
}
|
|
427
438
|
new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Realtime Database emulator is not running, so calls to Realtime Database will affect production.").log();
|
|
428
439
|
}
|
|
429
|
-
function warnAboutAuthProd(
|
|
430
|
-
if (
|
|
440
|
+
function warnAboutAuthProd() {
|
|
441
|
+
if (process.env[constants_1.Constants.FIREBASE_AUTH_EMULATOR_HOST]) {
|
|
431
442
|
return;
|
|
432
443
|
}
|
|
433
444
|
new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Firebase Authentication emulator is not running, so calls to Firebase Authentication will affect production.").log();
|
|
434
445
|
}
|
|
446
|
+
function warnAboutStorageProd() {
|
|
447
|
+
if (process.env[constants_1.Constants.FIREBASE_STORAGE_EMULATOR_HOST]) {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Firebase Storage emulator is not running, so calls to Firebase Storage will affect production.").log();
|
|
451
|
+
}
|
|
435
452
|
async function initializeFunctionsConfigHelper(frb) {
|
|
436
453
|
const functionsResolution = await assertResolveDeveloperNodeModule(frb, "firebase-functions");
|
|
437
454
|
const localFunctionsModule = require(functionsResolution.resolution);
|
|
@@ -467,7 +484,7 @@ async function initializeFunctionsConfigHelper(frb) {
|
|
|
467
484
|
function rawBodySaver(req, res, buf) {
|
|
468
485
|
req.rawBody = buf;
|
|
469
486
|
}
|
|
470
|
-
async function processHTTPS(frb
|
|
487
|
+
async function processHTTPS(frb) {
|
|
471
488
|
const ephemeralServer = express();
|
|
472
489
|
const functionRouter = express.Router();
|
|
473
490
|
const socketPath = frb.socketPath;
|
|
@@ -479,7 +496,6 @@ async function processHTTPS(frb, trigger) {
|
|
|
479
496
|
const handler = async (req, res) => {
|
|
480
497
|
try {
|
|
481
498
|
logDebug(`Ephemeral server handling ${req.method} request`);
|
|
482
|
-
const func = trigger.getRawFunction();
|
|
483
499
|
res.on("finish", () => {
|
|
484
500
|
instance.close((err) => {
|
|
485
501
|
if (err) {
|
|
@@ -490,7 +506,7 @@ async function processHTTPS(frb, trigger) {
|
|
|
490
506
|
}
|
|
491
507
|
});
|
|
492
508
|
});
|
|
493
|
-
await runHTTPS([req, res]
|
|
509
|
+
await runHTTPS([req, res]);
|
|
494
510
|
}
|
|
495
511
|
catch (err) {
|
|
496
512
|
rejectEphemeralServer(err);
|
|
@@ -524,11 +540,11 @@ async function processHTTPS(frb, trigger) {
|
|
|
524
540
|
instance.on("error", rejectEphemeralServer);
|
|
525
541
|
});
|
|
526
542
|
}
|
|
527
|
-
async function processBackground(frb,
|
|
543
|
+
async function processBackground(frb, signature) {
|
|
528
544
|
const proto = frb.proto;
|
|
529
545
|
logDebug("ProcessBackground", proto);
|
|
530
546
|
if (signature === "cloudevent") {
|
|
531
|
-
return runCloudEvent(proto
|
|
547
|
+
return runCloudEvent(proto);
|
|
532
548
|
}
|
|
533
549
|
const data = proto.data;
|
|
534
550
|
delete proto.data;
|
|
@@ -539,7 +555,7 @@ async function processBackground(frb, trigger, signature) {
|
|
|
539
555
|
context.resource = context.resource.name;
|
|
540
556
|
}
|
|
541
557
|
}
|
|
542
|
-
await runBackground({ data, context }
|
|
558
|
+
await runBackground({ data, context });
|
|
543
559
|
}
|
|
544
560
|
async function runFunction(func) {
|
|
545
561
|
let caughtErr;
|
|
@@ -554,30 +570,30 @@ async function runFunction(func) {
|
|
|
554
570
|
throw caughtErr;
|
|
555
571
|
}
|
|
556
572
|
}
|
|
557
|
-
async function runBackground(proto
|
|
573
|
+
async function runBackground(proto) {
|
|
558
574
|
logDebug("RunBackground", proto);
|
|
559
575
|
await runFunction(() => {
|
|
560
|
-
return
|
|
576
|
+
return functionTrigger(proto.data, proto.context);
|
|
561
577
|
});
|
|
562
578
|
}
|
|
563
|
-
async function runCloudEvent(event
|
|
579
|
+
async function runCloudEvent(event) {
|
|
564
580
|
logDebug("RunCloudEvent", event);
|
|
565
581
|
await runFunction(() => {
|
|
566
|
-
return
|
|
582
|
+
return functionTrigger(event);
|
|
567
583
|
});
|
|
568
584
|
}
|
|
569
|
-
async function runHTTPS(args
|
|
585
|
+
async function runHTTPS(args) {
|
|
570
586
|
if (args.length < 2) {
|
|
571
587
|
throw new Error("Function must be passed 2 args.");
|
|
572
588
|
}
|
|
573
589
|
await runFunction(() => {
|
|
574
|
-
return
|
|
590
|
+
return functionTrigger(args[0], args[1]);
|
|
575
591
|
});
|
|
576
592
|
}
|
|
577
593
|
async function moduleResolutionDetective(frb, error) {
|
|
578
594
|
const clues = {
|
|
579
|
-
tsconfigJSON: await requireAsync("./tsconfig.json", { paths: [
|
|
580
|
-
packageJSON: await requireAsync("./package.json", { paths: [
|
|
595
|
+
tsconfigJSON: await requireAsync("./tsconfig.json", { paths: [process.cwd()] }).catch(noOp),
|
|
596
|
+
packageJSON: await requireAsync("./package.json", { paths: [process.cwd()] }).catch(noOp),
|
|
581
597
|
};
|
|
582
598
|
const isPotentially = {
|
|
583
599
|
typescript: false,
|
|
@@ -595,45 +611,54 @@ async function moduleResolutionDetective(frb, error) {
|
|
|
595
611
|
function logDebug(msg, data) {
|
|
596
612
|
new types_1.EmulatorLog("DEBUG", "runtime-status", `[${process.pid}] ${msg}`, data).log();
|
|
597
613
|
}
|
|
598
|
-
async function invokeTrigger(frb
|
|
599
|
-
|
|
600
|
-
throw new Error("frb.triggerId unexpectedly null");
|
|
601
|
-
}
|
|
602
|
-
new types_1.EmulatorLog("INFO", "runtime-status", `Beginning execution of "${frb.triggerId}"`, {
|
|
614
|
+
async function invokeTrigger(frb) {
|
|
615
|
+
new types_1.EmulatorLog("INFO", "runtime-status", `Beginning execution of "${FUNCTION_TARGET_NAME}"`, {
|
|
603
616
|
frb,
|
|
604
617
|
}).log();
|
|
605
|
-
|
|
606
|
-
logDebug("triggerDefinition", trigger.definition);
|
|
607
|
-
const signature = (0, functionsEmulatorShared_1.getSignatureType)(trigger.definition);
|
|
608
|
-
logDebug(`Running ${frb.triggerId} in signature ${signature}`);
|
|
618
|
+
logDebug(`Running ${FUNCTION_TARGET_NAME} in signature ${FUNCTION_SIGNATURE}`);
|
|
609
619
|
let seconds = 0;
|
|
610
620
|
const timerId = setInterval(() => {
|
|
611
621
|
seconds++;
|
|
612
622
|
}, 1000);
|
|
613
623
|
let timeoutId;
|
|
614
624
|
if (isFeatureEnabled(frb, "timeout")) {
|
|
625
|
+
let timeout = process.env.FUNCTIONS_EMULATOR_TIMEOUT_SECONDS || "60";
|
|
626
|
+
if (timeout.endsWith("s")) {
|
|
627
|
+
timeout = timeout.slice(0, -1);
|
|
628
|
+
}
|
|
629
|
+
const timeoutMs = parseInt(timeout, 10) * 1000;
|
|
615
630
|
timeoutId = setTimeout(() => {
|
|
616
|
-
new types_1.EmulatorLog("WARN", "runtime-status", `Your function timed out after ~${
|
|
631
|
+
new types_1.EmulatorLog("WARN", "runtime-status", `Your function timed out after ~${timeout}s. To configure this timeout, see
|
|
617
632
|
https://firebase.google.com/docs/functions/manage-functions#set_timeout_and_memory_allocation.`).log();
|
|
618
633
|
throw new Error("Function timed out.");
|
|
619
|
-
},
|
|
634
|
+
}, timeoutMs);
|
|
620
635
|
}
|
|
621
|
-
switch (
|
|
636
|
+
switch (FUNCTION_SIGNATURE) {
|
|
622
637
|
case "event":
|
|
623
638
|
case "cloudevent":
|
|
624
|
-
await processBackground(frb,
|
|
639
|
+
await processBackground(frb, FUNCTION_SIGNATURE);
|
|
625
640
|
break;
|
|
626
641
|
case "http":
|
|
627
|
-
await processHTTPS(frb
|
|
642
|
+
await processHTTPS(frb);
|
|
628
643
|
break;
|
|
629
644
|
}
|
|
630
645
|
if (timeoutId) {
|
|
631
646
|
clearTimeout(timeoutId);
|
|
632
647
|
}
|
|
633
648
|
clearInterval(timerId);
|
|
634
|
-
new types_1.EmulatorLog("INFO", "runtime-status", `Finished "${
|
|
649
|
+
new types_1.EmulatorLog("INFO", "runtime-status", `Finished "${FUNCTION_TARGET_NAME}" in ~${Math.max(seconds, 1)}s`).log();
|
|
635
650
|
}
|
|
636
|
-
async function initializeRuntime(frb
|
|
651
|
+
async function initializeRuntime(frb) {
|
|
652
|
+
FUNCTION_TARGET_NAME = process.env.FUNCTION_TARGET || "";
|
|
653
|
+
if (!FUNCTION_TARGET_NAME) {
|
|
654
|
+
new types_1.EmulatorLog("FATAL", "runtime-status", `Environment variable FUNCTION_TARGET cannot be empty. This shouldn't happen.`).log();
|
|
655
|
+
await flushAndExit(1);
|
|
656
|
+
}
|
|
657
|
+
FUNCTION_SIGNATURE = process.env.FUNCTION_SIGNATURE_TYPE || "";
|
|
658
|
+
if (!FUNCTION_SIGNATURE) {
|
|
659
|
+
new types_1.EmulatorLog("FATAL", "runtime-status", `Environment variable FUNCTION_SIGNATURE_TYPE cannot be empty. This shouldn't happen.`).log();
|
|
660
|
+
await flushAndExit(1);
|
|
661
|
+
}
|
|
637
662
|
logDebug(`Disabled runtime features: ${JSON.stringify(frb.disabled_features)}`);
|
|
638
663
|
const verified = await verifyDeveloperNodeModules(frb);
|
|
639
664
|
if (!verified) {
|
|
@@ -645,35 +670,33 @@ async function initializeRuntime(frb, serializedFunctionTrigger, extensionTrigge
|
|
|
645
670
|
await initializeFunctionsConfigHelper(frb);
|
|
646
671
|
await initializeFirebaseFunctionsStubs(frb);
|
|
647
672
|
await initializeFirebaseAdminStubs(frb);
|
|
648
|
-
|
|
673
|
+
}
|
|
674
|
+
async function loadTrigger(frb, functionTarget, serializedFunctionTrigger) {
|
|
649
675
|
let triggerModule;
|
|
650
676
|
if (serializedFunctionTrigger) {
|
|
651
677
|
triggerModule = eval(serializedFunctionTrigger)();
|
|
652
678
|
}
|
|
653
679
|
else {
|
|
654
680
|
try {
|
|
655
|
-
triggerModule = require(
|
|
681
|
+
triggerModule = require(process.cwd());
|
|
656
682
|
}
|
|
657
683
|
catch (err) {
|
|
658
684
|
if (err.code !== "ERR_REQUIRE_ESM") {
|
|
659
685
|
await moduleResolutionDetective(frb, err);
|
|
660
|
-
|
|
686
|
+
throw err;
|
|
661
687
|
}
|
|
662
|
-
const modulePath = require.resolve(
|
|
688
|
+
const modulePath = require.resolve(process.cwd());
|
|
663
689
|
const moduleURL = (0, url_1.pathToFileURL)(modulePath).href;
|
|
664
690
|
triggerModule = await dynamicImport(moduleURL);
|
|
665
691
|
}
|
|
666
692
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
|
|
693
|
+
const maybeTrigger = functionTarget.split(".").reduce((mod, functionTargetPart) => {
|
|
694
|
+
return mod === null || mod === void 0 ? void 0 : mod[functionTargetPart];
|
|
695
|
+
}, triggerModule);
|
|
696
|
+
if (!maybeTrigger) {
|
|
697
|
+
throw new Error(`Failed to find function ${functionTarget} in the loaded module`);
|
|
672
698
|
}
|
|
673
|
-
|
|
674
|
-
const triggers = (0, functionsEmulatorShared_1.getEmulatedTriggersFromDefinitions)(triggerDefinitions, triggerModule);
|
|
675
|
-
new types_1.EmulatorLog("SYSTEM", "triggers-parsed", "", { triggers, triggerDefinitions }).log();
|
|
676
|
-
return triggers;
|
|
699
|
+
return maybeTrigger;
|
|
677
700
|
}
|
|
678
701
|
async function flushAndExit(code) {
|
|
679
702
|
await types_1.EmulatorLog.waitForFlush();
|
|
@@ -693,28 +716,22 @@ async function handleMessage(message) {
|
|
|
693
716
|
await flushAndExit(1);
|
|
694
717
|
return;
|
|
695
718
|
}
|
|
696
|
-
if (!
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
}
|
|
709
|
-
if (!triggers[runtimeArgs.frb.triggerId]) {
|
|
710
|
-
new types_1.EmulatorLog("FATAL", "runtime-status", `Could not find trigger "${runtimeArgs.frb.triggerId}" in your functions directory.`).log();
|
|
711
|
-
return;
|
|
712
|
-
}
|
|
713
|
-
else {
|
|
714
|
-
logDebug(`Trigger "${runtimeArgs.frb.triggerId}" has been found, beginning invocation!`);
|
|
719
|
+
if (!functionTrigger) {
|
|
720
|
+
try {
|
|
721
|
+
await initializeRuntime(runtimeArgs.frb);
|
|
722
|
+
const serializedTriggers = runtimeArgs.opts ? runtimeArgs.opts.serializedTriggers : undefined;
|
|
723
|
+
functionTrigger = await loadTrigger(runtimeArgs.frb, FUNCTION_TARGET_NAME, serializedTriggers);
|
|
724
|
+
}
|
|
725
|
+
catch (e) {
|
|
726
|
+
logDebug(e);
|
|
727
|
+
new types_1.EmulatorLog("FATAL", "runtime-status", `Failed to initialize and load trigger. This shouldn't happen: ${e.message}`).log();
|
|
728
|
+
await flushAndExit(1);
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
715
731
|
}
|
|
732
|
+
logDebug(`Beginning invocation function ${FUNCTION_TARGET_NAME}!`);
|
|
716
733
|
try {
|
|
717
|
-
await invokeTrigger(runtimeArgs.frb
|
|
734
|
+
await invokeTrigger(runtimeArgs.frb);
|
|
718
735
|
if (runtimeArgs.opts && runtimeArgs.opts.serializedTriggers) {
|
|
719
736
|
await flushAndExit(0);
|
|
720
737
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.EmulatedTrigger = exports.HttpConstants = void 0;
|
|
3
|
+
exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.emulatedFunctionsFromEndpoints = exports.EmulatedTrigger = exports.HttpConstants = void 0;
|
|
4
4
|
const _ = require("lodash");
|
|
5
5
|
const os = require("os");
|
|
6
6
|
const path = require("path");
|
|
7
7
|
const fs = require("fs");
|
|
8
8
|
const constants_1 = require("./constants");
|
|
9
|
+
const backend_1 = require("../deploy/functions/backend");
|
|
10
|
+
const proto_1 = require("../gcp/proto");
|
|
9
11
|
const memoryLookup = {
|
|
10
12
|
"128MB": 128,
|
|
11
13
|
"256MB": 256,
|
|
@@ -44,6 +46,54 @@ class EmulatedTrigger {
|
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
exports.EmulatedTrigger = EmulatedTrigger;
|
|
49
|
+
function emulatedFunctionsFromEndpoints(endpoints) {
|
|
50
|
+
const regionDefinitions = [];
|
|
51
|
+
for (const endpoint of endpoints) {
|
|
52
|
+
if (!endpoint.region) {
|
|
53
|
+
endpoint.region = "us-central1";
|
|
54
|
+
}
|
|
55
|
+
const def = {
|
|
56
|
+
entryPoint: endpoint.entryPoint,
|
|
57
|
+
platform: endpoint.platform,
|
|
58
|
+
region: endpoint.region,
|
|
59
|
+
name: endpoint.id,
|
|
60
|
+
id: `${endpoint.region}-${endpoint.id}`,
|
|
61
|
+
};
|
|
62
|
+
(0, proto_1.copyIfPresent)(def, endpoint, "timeout", "availableMemoryMb", "labels", "platform", "secretEnvironmentVariables");
|
|
63
|
+
if ((0, backend_1.isHttpsTriggered)(endpoint)) {
|
|
64
|
+
def.httpsTrigger = endpoint.httpsTrigger;
|
|
65
|
+
}
|
|
66
|
+
else if ((0, backend_1.isEventTriggered)(endpoint)) {
|
|
67
|
+
const eventTrigger = endpoint.eventTrigger;
|
|
68
|
+
if (endpoint.platform === "gcfv1") {
|
|
69
|
+
def.eventTrigger = {
|
|
70
|
+
eventType: eventTrigger.eventType,
|
|
71
|
+
resource: eventTrigger.eventFilters.resource,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
const { resource, topic, bucket } = endpoint.eventTrigger.eventFilters;
|
|
76
|
+
const eventResource = resource || topic || bucket;
|
|
77
|
+
if (!eventResource) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
def.eventTrigger = {
|
|
81
|
+
eventType: eventTrigger.eventType,
|
|
82
|
+
resource: eventResource,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else if ((0, backend_1.isScheduleTriggered)(endpoint)) {
|
|
87
|
+
def.eventTrigger = { eventType: "pubsub", resource: "" };
|
|
88
|
+
def.schedule = endpoint.scheduleTrigger;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
}
|
|
92
|
+
regionDefinitions.push(def);
|
|
93
|
+
}
|
|
94
|
+
return regionDefinitions;
|
|
95
|
+
}
|
|
96
|
+
exports.emulatedFunctionsFromEndpoints = emulatedFunctionsFromEndpoints;
|
|
47
97
|
function emulatedFunctionsByRegion(definitions) {
|
|
48
98
|
const regionDefinitions = [];
|
|
49
99
|
for (const def of definitions) {
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.FunctionsEmulatorShell = void 0;
|
|
4
4
|
const uuid = require("uuid");
|
|
5
5
|
const functionsEmulator_1 = require("./functionsEmulator");
|
|
6
|
-
const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
|
|
7
6
|
const utils = require("../utils");
|
|
8
7
|
const logger_1 = require("../logger");
|
|
9
8
|
const error_1 = require("../error");
|
|
@@ -43,7 +42,7 @@ class FunctionsEmulatorShell {
|
|
|
43
42
|
auth: opts.auth,
|
|
44
43
|
data,
|
|
45
44
|
};
|
|
46
|
-
this.emu.startFunctionRuntime(this.backend, trigger
|
|
45
|
+
this.emu.startFunctionRuntime(this.backend, trigger, proto);
|
|
47
46
|
}
|
|
48
47
|
getTrigger(name) {
|
|
49
48
|
const result = this.triggers.find((trigger) => {
|
|
@@ -43,7 +43,7 @@ class RuntimeWorker {
|
|
|
43
43
|
execute(frb, opts) {
|
|
44
44
|
const execFrb = Object.assign({}, frb);
|
|
45
45
|
if (!execFrb.socketPath) {
|
|
46
|
-
execFrb.socketPath = (0, functionsEmulatorShared_1.getTemporarySocketPath)(this.runtime.pid,
|
|
46
|
+
execFrb.socketPath = (0, functionsEmulatorShared_1.getTemporarySocketPath)(this.runtime.pid, this.runtime.cwd);
|
|
47
47
|
this.log(`Assigning socketPath: ${execFrb.socketPath}`);
|
|
48
48
|
}
|
|
49
49
|
const args = { frb: execFrb, opts };
|
|
@@ -14,10 +14,10 @@ function createCloudEndpoints(emulator) {
|
|
|
14
14
|
storageLayer.createBucket(req.params[0]);
|
|
15
15
|
next();
|
|
16
16
|
});
|
|
17
|
-
gcloudStorageAPI.get("/b", (req, res) => {
|
|
17
|
+
gcloudStorageAPI.get("/b", async (req, res) => {
|
|
18
18
|
res.json({
|
|
19
19
|
kind: "storage#buckets",
|
|
20
|
-
items: storageLayer.listBuckets(),
|
|
20
|
+
items: await storageLayer.listBuckets(),
|
|
21
21
|
});
|
|
22
22
|
});
|
|
23
23
|
gcloudStorageAPI.get(["/b/:bucketId/o/:objectId", "/download/storage/v1/b/:bucketId/o/:objectId"], (req, res) => {
|
|
@@ -19,6 +19,7 @@ const fse = require("fs-extra");
|
|
|
19
19
|
const rimraf = require("rimraf");
|
|
20
20
|
const cloudFunctions_1 = require("./cloudFunctions");
|
|
21
21
|
const logger_1 = require("../../logger");
|
|
22
|
+
const adminSdkConfig_1 = require("../adminSdkConfig");
|
|
22
23
|
class StoredFile {
|
|
23
24
|
constructor(metadata, path) {
|
|
24
25
|
this.metadata = metadata;
|
|
@@ -108,9 +109,13 @@ class StorageLayer {
|
|
|
108
109
|
this._buckets.set(id, new metadata_1.CloudStorageBucketMetadata(id));
|
|
109
110
|
}
|
|
110
111
|
}
|
|
111
|
-
listBuckets() {
|
|
112
|
+
async listBuckets() {
|
|
112
113
|
if (this._buckets.size == 0) {
|
|
113
|
-
this.
|
|
114
|
+
let adminSdkConfig = await (0, adminSdkConfig_1.getProjectAdminSdkConfigOrCached)(this._projectId);
|
|
115
|
+
if (!adminSdkConfig) {
|
|
116
|
+
adminSdkConfig = (0, adminSdkConfig_1.constructDefaultAdminSdkConfig)(this._projectId);
|
|
117
|
+
}
|
|
118
|
+
this.createBucket(adminSdkConfig.storageBucket);
|
|
114
119
|
}
|
|
115
120
|
return [...this._buckets.values()];
|
|
116
121
|
}
|
|
@@ -355,7 +360,7 @@ class StorageLayer {
|
|
|
355
360
|
const bucketsList = {
|
|
356
361
|
buckets: [],
|
|
357
362
|
};
|
|
358
|
-
for (const b of this.listBuckets()) {
|
|
363
|
+
for (const b of await this.listBuckets()) {
|
|
359
364
|
bucketsList.buckets.push({ id: b.id });
|
|
360
365
|
}
|
|
361
366
|
const bucketsFilePath = path.join(storageExportPath, "buckets.json");
|
|
@@ -199,7 +199,7 @@ async function generateSecretName(projectId, instanceId, paramName) {
|
|
|
199
199
|
return secretName;
|
|
200
200
|
}
|
|
201
201
|
async function addNewSecretVersion(projectId, instanceId, secret, paramSpec, secretValue) {
|
|
202
|
-
const version = await secretManagerApi.addVersion(secret, secretValue);
|
|
202
|
+
const version = await secretManagerApi.addVersion(projectId, secret.name, secretValue);
|
|
203
203
|
await secretsUtils.grantFirexServiceAgentSecretAdminRole(secret);
|
|
204
204
|
return `projects/${version.secret.projectId}/secrets/${version.secret.name}/versions/${version.versionId}`;
|
|
205
205
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.diagnose = void 0;
|
|
4
|
+
const extensionsHelper_1 = require("./extensionsHelper");
|
|
5
|
+
const getProjectNumber_1 = require("../getProjectNumber");
|
|
6
|
+
const utils = require("../utils");
|
|
7
|
+
const resourceManager = require("../gcp/resourceManager");
|
|
8
|
+
const extensionsApi_1 = require("./extensionsApi");
|
|
9
|
+
const prompt_1 = require("../prompt");
|
|
10
|
+
const logger_1 = require("../logger");
|
|
11
|
+
const error_1 = require("../error");
|
|
12
|
+
const SERVICE_AGENT_ROLE = "roles/firebasemods.serviceAgent";
|
|
13
|
+
async function diagnose(projectId) {
|
|
14
|
+
const projectNumber = await (0, getProjectNumber_1.getProjectNumber)({ projectId });
|
|
15
|
+
const firexSaProjectId = utils.envOverride("FIREBASE_EXTENSIONS_SA_PROJECT_ID", "gcp-sa-firebasemods");
|
|
16
|
+
const saEmail = `service-${projectNumber}@${firexSaProjectId}.iam.gserviceaccount.com`;
|
|
17
|
+
utils.logLabeledBullet(extensionsHelper_1.logPrefix, "Checking project IAM policy...");
|
|
18
|
+
await (0, extensionsApi_1.listInstances)(projectId);
|
|
19
|
+
let policy;
|
|
20
|
+
try {
|
|
21
|
+
policy = await resourceManager.getIamPolicy(projectId);
|
|
22
|
+
logger_1.logger.debug(policy);
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
if (e instanceof error_1.FirebaseError && e.status === 403) {
|
|
26
|
+
throw new error_1.FirebaseError("Unable to get project IAM policy, permission denied (403). Please " +
|
|
27
|
+
"make sure you have sufficient project privileges or if this is a brand new project " +
|
|
28
|
+
"try again in a few minutes.");
|
|
29
|
+
}
|
|
30
|
+
throw e;
|
|
31
|
+
}
|
|
32
|
+
if (policy.bindings.find((b) => b.role === SERVICE_AGENT_ROLE && b.members.includes("serviceAccount:" + saEmail))) {
|
|
33
|
+
utils.logLabeledSuccess(extensionsHelper_1.logPrefix, "Project IAM policy OK");
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
utils.logWarning("Firebase Extensions Service Agent is missing a required IAM role " +
|
|
38
|
+
"`Firebase Extensions API Service Agent`.");
|
|
39
|
+
const fix = await (0, prompt_1.promptOnce)({
|
|
40
|
+
type: "confirm",
|
|
41
|
+
message: "Would you like to fix the issue by updating IAM policy to include Firebase " +
|
|
42
|
+
"Extensions Service Agent with role `Firebase Extensions API Service Agent`",
|
|
43
|
+
});
|
|
44
|
+
if (fix) {
|
|
45
|
+
policy.bindings.push({
|
|
46
|
+
role: SERVICE_AGENT_ROLE,
|
|
47
|
+
members: ["serviceAccount:" + saEmail],
|
|
48
|
+
});
|
|
49
|
+
await resourceManager.setIamPolicy(projectId, policy, "bindings");
|
|
50
|
+
utils.logSuccess("Project IAM policy updated successfully");
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.diagnose = diagnose;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.confirm = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.
|
|
3
|
+
exports.diagnoseAndFixProject = exports.confirm = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.publishExtensionVersionFromLocalSource = exports.ensureExtensionsApiEnabled = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
|
|
4
4
|
const _ = require("lodash");
|
|
5
5
|
const clc = require("cli-color");
|
|
6
6
|
const ora = require("ora");
|
|
@@ -16,6 +16,7 @@ const utils_1 = require("./utils");
|
|
|
16
16
|
const functionsConfig_1 = require("../functionsConfig");
|
|
17
17
|
const resolveSource_1 = require("./resolveSource");
|
|
18
18
|
const error_1 = require("../error");
|
|
19
|
+
const diagnose_1 = require("./diagnose");
|
|
19
20
|
const askUserForParam_1 = require("./askUserForParam");
|
|
20
21
|
const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
21
22
|
const storage_1 = require("../gcp/storage");
|
|
@@ -382,22 +383,6 @@ async function deleteUploadedSource(objectPath) {
|
|
|
382
383
|
}
|
|
383
384
|
}
|
|
384
385
|
}
|
|
385
|
-
async function getExtensionSourceFromName(extensionName) {
|
|
386
|
-
const officialExtensionRegex = /^[a-zA-Z\-]+[0-9@.]*$/;
|
|
387
|
-
const existingSourceRegex = /projects\/.+\/sources\/.+/;
|
|
388
|
-
if (officialExtensionRegex.test(extensionName)) {
|
|
389
|
-
const [name, version] = extensionName.split("@");
|
|
390
|
-
const registryEntry = await (0, resolveSource_1.resolveRegistryEntry)(name);
|
|
391
|
-
const sourceUrl = (0, resolveSource_1.resolveSourceUrl)(registryEntry, name, version);
|
|
392
|
-
return await (0, extensionsApi_1.getSource)(sourceUrl);
|
|
393
|
-
}
|
|
394
|
-
else if (existingSourceRegex.test(extensionName)) {
|
|
395
|
-
logger_1.logger.info(`Fetching the source "${extensionName}"...`);
|
|
396
|
-
return await (0, extensionsApi_1.getSource)(extensionName);
|
|
397
|
-
}
|
|
398
|
-
throw new error_1.FirebaseError(`Could not find an extension named '${extensionName}'. `);
|
|
399
|
-
}
|
|
400
|
-
exports.getExtensionSourceFromName = getExtensionSourceFromName;
|
|
401
386
|
function getPublisherProjectFromName(publisherName) {
|
|
402
387
|
const publisherNameRegex = /projects\/.+\/publisherProfile/;
|
|
403
388
|
if (publisherNameRegex.test(publisherName)) {
|
|
@@ -520,3 +505,11 @@ async function confirm(args) {
|
|
|
520
505
|
}
|
|
521
506
|
}
|
|
522
507
|
exports.confirm = confirm;
|
|
508
|
+
async function diagnoseAndFixProject(options) {
|
|
509
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
510
|
+
const ok = await (0, diagnose_1.diagnose)(projectId);
|
|
511
|
+
if (!ok) {
|
|
512
|
+
throw new error_1.FirebaseError("Unable to proceed until all issues are resolved.");
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
exports.diagnoseAndFixProject = diagnoseAndFixProject;
|