firebase-tools 11.17.0 → 11.19.0
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 +3 -2
- package/lib/commands/index.js +5 -0
- package/lib/commands/internaltesting-functions-discover.js +25 -0
- package/lib/deploy/extensions/prepare.js +6 -1
- package/lib/deploy/extensions/v2FunctionHelper.js +53 -0
- package/lib/deploy/functions/build.js +17 -2
- package/lib/deploy/functions/cel.js +90 -13
- package/lib/deploy/functions/params.js +141 -6
- package/lib/deploy/functions/prepare.js +40 -25
- package/lib/deploy/functions/release/fabricator.js +27 -1
- package/lib/deploy/functions/runtimes/discovery/parsing.js +7 -1
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +1 -1
- package/lib/deploy/functions/runtimes/index.js +2 -1
- package/lib/deploy/functions/runtimes/node/index.js +2 -2
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +1 -0
- package/lib/deploy/functions/runtimes/node/versioning.js +30 -14
- package/lib/emulator/commandUtils.js +2 -1
- package/lib/emulator/downloadableEmulators.js +3 -3
- package/lib/emulator/env.js +29 -27
- package/lib/emulator/extensions/validation.js +2 -0
- package/lib/emulator/extensionsEmulator.js +1 -1
- package/lib/emulator/functionsEmulator.js +44 -32
- package/lib/emulator/storage/rules/runtime.js +2 -2
- package/lib/emulator/workQueue.js +11 -6
- package/lib/experiments.js +6 -0
- package/lib/extensions/billingMigrationHelper.js +2 -1
- package/lib/extensions/displayExtensionInfo.js +2 -1
- package/lib/extensions/emulator/specHelper.js +4 -3
- package/lib/extensions/emulator/triggerHelper.js +64 -20
- package/lib/extensions/extensionsHelper.js +1 -4
- package/lib/extensions/types.js +2 -1
- package/lib/extensions/utils.js +14 -1
- package/lib/firestore/indexes-api.js +7 -1
- package/lib/firestore/indexes-sort.js +4 -0
- package/lib/firestore/indexes.js +31 -5
- package/lib/firestore/util.js +5 -1
- package/lib/firestore/validator.js +7 -1
- package/lib/frameworks/angular/index.js +3 -0
- package/lib/frameworks/index.js +7 -1
- package/lib/frameworks/next/constants.js +10 -0
- package/lib/frameworks/next/index.js +170 -127
- package/lib/frameworks/next/interfaces.js +2 -0
- package/lib/frameworks/next/utils.js +92 -0
- package/lib/frameworks/nuxt/index.js +3 -0
- package/lib/frameworks/utils.js +24 -0
- package/lib/frameworks/vite/index.js +4 -1
- package/lib/gcp/eventarc.js +42 -0
- package/lib/init/features/emulators.js +1 -1
- package/npm-shrinkwrap.json +2 -93
- package/package.json +1 -4
- package/schema/firebase-config.json +4 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveCpuAndConcurrency = exports.inferBlockingDetails = exports.updateEndpointTargetedStatus = exports.inferDetailsFromExisting = exports.prepare = exports.EVENTARC_SOURCE_ENV = void 0;
|
|
3
|
+
exports.loadCodebases = exports.resolveCpuAndConcurrency = exports.inferBlockingDetails = exports.updateEndpointTargetedStatus = exports.inferDetailsFromExisting = exports.prepare = exports.EVENTARC_SOURCE_ENV = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
5
|
const backend = require("./backend");
|
|
6
6
|
const build = require("./build");
|
|
@@ -31,7 +31,7 @@ function hasUserConfig(config) {
|
|
|
31
31
|
return Object.keys(config).length > 1;
|
|
32
32
|
}
|
|
33
33
|
async function prepare(context, options, payload) {
|
|
34
|
-
var _a;
|
|
34
|
+
var _a, _b;
|
|
35
35
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
36
36
|
const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
|
|
37
37
|
context.config = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions);
|
|
@@ -40,6 +40,9 @@ async function prepare(context, options, payload) {
|
|
|
40
40
|
if (codebases.length === 0) {
|
|
41
41
|
throw new error_1.FirebaseError("No function matches given --only filters. Aborting deployment.");
|
|
42
42
|
}
|
|
43
|
+
for (const codebase of codebases) {
|
|
44
|
+
(0, utils_1.logLabeledBullet)("functions", `preparing codebase ${clc.bold(codebase)} for deployment`);
|
|
45
|
+
}
|
|
43
46
|
const checkAPIsEnabled = await Promise.all([
|
|
44
47
|
ensureApiEnabled.ensure(projectId, "cloudfunctions.googleapis.com", "functions"),
|
|
45
48
|
ensureApiEnabled.check(projectId, "runtimeconfig.googleapis.com", "runtimeconfig", true),
|
|
@@ -52,45 +55,28 @@ async function prepare(context, options, payload) {
|
|
|
52
55
|
if (checkAPIsEnabled[1]) {
|
|
53
56
|
runtimeConfig = Object.assign(Object.assign({}, runtimeConfig), (await (0, prepareFunctionsUpload_1.getFunctionsConfig)(projectId)));
|
|
54
57
|
}
|
|
55
|
-
context.
|
|
58
|
+
const wantBuilds = await loadCodebases(context.config, options, firebaseConfig, runtimeConfig, context.filters);
|
|
56
59
|
const codebaseUsesEnvs = [];
|
|
57
60
|
const wantBackends = {};
|
|
58
|
-
for (const codebase of
|
|
59
|
-
(0, utils_1.logLabeledBullet)("functions", `preparing codebase ${clc.bold(codebase)} for deployment`);
|
|
61
|
+
for (const [codebase, wantBuild] of Object.entries(wantBuilds)) {
|
|
60
62
|
const config = (0, projectConfig_1.configForCodebase)(context.config, codebase);
|
|
61
|
-
const sourceDirName = config.source;
|
|
62
|
-
if (!sourceDirName) {
|
|
63
|
-
throw new error_1.FirebaseError(`No functions code detected at default location (./functions), and no functions source defined in firebase.json`);
|
|
64
|
-
}
|
|
65
|
-
const sourceDir = options.config.path(sourceDirName);
|
|
66
|
-
const delegateContext = {
|
|
67
|
-
projectId,
|
|
68
|
-
sourceDir,
|
|
69
|
-
projectDir: options.config.projectDir,
|
|
70
|
-
runtime: config.runtime || "",
|
|
71
|
-
};
|
|
72
|
-
const runtimeDelegate = await runtimes.getRuntimeDelegate(delegateContext);
|
|
73
|
-
logger_1.logger.debug(`Validating ${runtimeDelegate.name} source`);
|
|
74
|
-
await runtimeDelegate.validate();
|
|
75
|
-
logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
|
|
76
|
-
await runtimeDelegate.build();
|
|
77
63
|
const firebaseEnvs = functionsEnv.loadFirebaseEnvs(firebaseConfig, projectId);
|
|
78
64
|
const userEnvOpt = {
|
|
79
|
-
functionsSource:
|
|
65
|
+
functionsSource: options.config.path(config.source),
|
|
80
66
|
projectId: projectId,
|
|
81
67
|
projectAlias: options.projectAlias,
|
|
82
68
|
};
|
|
83
69
|
const userEnvs = functionsEnv.loadUserEnvs(userEnvOpt);
|
|
84
70
|
const envs = Object.assign(Object.assign({}, userEnvs), firebaseEnvs);
|
|
85
|
-
const wantBuild = await runtimeDelegate.discoverBuild(runtimeConfig, firebaseEnvs);
|
|
86
71
|
const { backend: wantBackend, envs: resolvedEnvs } = await build.resolveBackend(wantBuild, firebaseConfig, userEnvOpt, userEnvs, options.nonInteractive);
|
|
87
72
|
let hasEnvsFromParams = false;
|
|
88
73
|
wantBackend.environmentVariables = envs;
|
|
89
74
|
for (const envName of Object.keys(resolvedEnvs)) {
|
|
90
|
-
const
|
|
75
|
+
const isList = (_a = resolvedEnvs[envName]) === null || _a === void 0 ? void 0 : _a.legalList;
|
|
76
|
+
const envValue = (_b = resolvedEnvs[envName]) === null || _b === void 0 ? void 0 : _b.toSDK();
|
|
91
77
|
if (envValue &&
|
|
92
78
|
!resolvedEnvs[envName].internal &&
|
|
93
|
-
!Object.prototype.hasOwnProperty.call(wantBackend.environmentVariables, envName)) {
|
|
79
|
+
(!Object.prototype.hasOwnProperty.call(wantBackend.environmentVariables, envName) || isList)) {
|
|
94
80
|
wantBackend.environmentVariables[envName] = envValue;
|
|
95
81
|
hasEnvsFromParams = true;
|
|
96
82
|
}
|
|
@@ -127,6 +113,7 @@ async function prepare(context, options, payload) {
|
|
|
127
113
|
}
|
|
128
114
|
}
|
|
129
115
|
validate.endpointsAreUnique(wantBackends);
|
|
116
|
+
context.sources = {};
|
|
130
117
|
for (const [codebase, wantBackend] of Object.entries(wantBackends)) {
|
|
131
118
|
const config = (0, projectConfig_1.configForCodebase)(context.config, codebase);
|
|
132
119
|
const sourceDirName = config.source;
|
|
@@ -291,3 +278,31 @@ function resolveCpuAndConcurrency(want) {
|
|
|
291
278
|
}
|
|
292
279
|
}
|
|
293
280
|
exports.resolveCpuAndConcurrency = resolveCpuAndConcurrency;
|
|
281
|
+
async function loadCodebases(config, options, firebaseConfig, runtimeConfig, filters) {
|
|
282
|
+
const codebases = (0, functionsDeployHelper_1.targetCodebases)(config, filters);
|
|
283
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
284
|
+
const wantBuilds = {};
|
|
285
|
+
for (const codebase of codebases) {
|
|
286
|
+
const codebaseConfig = (0, projectConfig_1.configForCodebase)(config, codebase);
|
|
287
|
+
const sourceDirName = codebaseConfig.source;
|
|
288
|
+
if (!sourceDirName) {
|
|
289
|
+
throw new error_1.FirebaseError(`No functions code detected at default location (./functions), and no functions source defined in firebase.json`);
|
|
290
|
+
}
|
|
291
|
+
const sourceDir = options.config.path(sourceDirName);
|
|
292
|
+
const delegateContext = {
|
|
293
|
+
projectId,
|
|
294
|
+
sourceDir,
|
|
295
|
+
projectDir: options.config.projectDir,
|
|
296
|
+
runtime: codebaseConfig.runtime || "",
|
|
297
|
+
};
|
|
298
|
+
const runtimeDelegate = await runtimes.getRuntimeDelegate(delegateContext);
|
|
299
|
+
logger_1.logger.debug(`Validating ${runtimeDelegate.name} source`);
|
|
300
|
+
await runtimeDelegate.validate();
|
|
301
|
+
logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
|
|
302
|
+
await runtimeDelegate.build();
|
|
303
|
+
const firebaseEnvs = functionsEnv.loadFirebaseEnvs(firebaseConfig, projectId);
|
|
304
|
+
wantBuilds[codebase] = await runtimeDelegate.discoverBuild(runtimeConfig, firebaseEnvs);
|
|
305
|
+
}
|
|
306
|
+
return wantBuilds;
|
|
307
|
+
}
|
|
308
|
+
exports.loadCodebases = loadCodebases;
|
|
@@ -14,6 +14,7 @@ const cloudtasks = require("../../../gcp/cloudtasks");
|
|
|
14
14
|
const deploymentTool = require("../../../deploymentTool");
|
|
15
15
|
const gcf = require("../../../gcp/cloudfunctions");
|
|
16
16
|
const gcfV2 = require("../../../gcp/cloudfunctionsv2");
|
|
17
|
+
const eventarc = require("../../../gcp/eventarc");
|
|
17
18
|
const helper = require("../functionsDeployHelper");
|
|
18
19
|
const poller = require("../../../operation-poller");
|
|
19
20
|
const pubsub = require("../../../gcp/pubsub");
|
|
@@ -36,6 +37,12 @@ const gcfV2PollerOptions = {
|
|
|
36
37
|
masterTimeout: 25 * 60 * 1000,
|
|
37
38
|
maxBackoff: 10000,
|
|
38
39
|
};
|
|
40
|
+
const eventarcPollerOptions = {
|
|
41
|
+
apiOrigin: api_1.eventarcOrigin,
|
|
42
|
+
apiVersion: "v1",
|
|
43
|
+
masterTimeout: 25 * 60 * 1000,
|
|
44
|
+
maxBackoff: 10000,
|
|
45
|
+
};
|
|
39
46
|
const rethrowAs = (endpoint, op) => (err) => {
|
|
40
47
|
logger_1.logger.error(err.message);
|
|
41
48
|
throw new reporter.DeploymentError(endpoint, op, err);
|
|
@@ -212,7 +219,7 @@ class Fabricator {
|
|
|
212
219
|
}
|
|
213
220
|
}
|
|
214
221
|
async createV2Function(endpoint) {
|
|
215
|
-
var _a, _b;
|
|
222
|
+
var _a, _b, _c;
|
|
216
223
|
const storage = (_a = this.sources[endpoint.codebase]) === null || _a === void 0 ? void 0 : _a.storage;
|
|
217
224
|
if (!storage) {
|
|
218
225
|
logger_1.logger.debug("Precondition failed. Cannot create a GCFv2 function without storage");
|
|
@@ -237,6 +244,25 @@ class Fabricator {
|
|
|
237
244
|
})
|
|
238
245
|
.catch(rethrowAs(endpoint, "create topic"));
|
|
239
246
|
}
|
|
247
|
+
const channel = (_c = apiFunction.eventTrigger) === null || _c === void 0 ? void 0 : _c.channel;
|
|
248
|
+
if (channel) {
|
|
249
|
+
await this.executor
|
|
250
|
+
.run(async () => {
|
|
251
|
+
try {
|
|
252
|
+
const op = await eventarc.createChannel({ name: channel });
|
|
253
|
+
return await poller.pollOperation(Object.assign(Object.assign({}, eventarcPollerOptions), { pollerName: `create-${channel}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name }));
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
if (err.status === 409) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
throw new error_1.FirebaseError("Unexpected error creating Eventarc channel", {
|
|
260
|
+
original: err,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
.catch(rethrowAs(endpoint, "upsert eventarc channel"));
|
|
265
|
+
}
|
|
240
266
|
const resultFunction = await this.functionExecutor
|
|
241
267
|
.run(async () => {
|
|
242
268
|
const op = await gcfV2.createFunction(apiFunction);
|
|
@@ -8,7 +8,7 @@ function requireKeys(prefix, yaml, ...keys) {
|
|
|
8
8
|
}
|
|
9
9
|
for (const key of keys) {
|
|
10
10
|
if (!yaml[key]) {
|
|
11
|
-
throw new error_1.FirebaseError(`Expected key ${prefix + key}`);
|
|
11
|
+
throw new error_1.FirebaseError(`Expected key ${prefix + key.toString()}`);
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -47,6 +47,12 @@ function assertKeyTypes(prefix, yaml, schema) {
|
|
|
47
47
|
}
|
|
48
48
|
continue;
|
|
49
49
|
}
|
|
50
|
+
if (schemaType === "List") {
|
|
51
|
+
if (typeof value !== "string" && !Array.isArray(value)) {
|
|
52
|
+
throw new error_1.FirebaseError(`Expected ${fullKey} to be a field list (array or list expression); was ${typeof value}`);
|
|
53
|
+
}
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
50
56
|
if (value === null) {
|
|
51
57
|
if (schemaType.endsWith("?")) {
|
|
52
58
|
continue;
|
|
@@ -48,7 +48,7 @@ function parseRequiredAPIs(manifest) {
|
|
|
48
48
|
function assertBuildEndpoint(ep, id) {
|
|
49
49
|
const prefix = `endpoints[${id}]`;
|
|
50
50
|
(0, parsing_1.assertKeyTypes)(prefix, ep, {
|
|
51
|
-
region: "
|
|
51
|
+
region: "List",
|
|
52
52
|
platform: (platform) => build.AllFunctionsPlatforms.includes(platform),
|
|
53
53
|
entryPoint: "string",
|
|
54
54
|
omit: "Field<boolean>?",
|
|
@@ -4,7 +4,7 @@ exports.getRuntimeDelegate = exports.getHumanFriendlyRuntimeName = exports.isVal
|
|
|
4
4
|
const node = require("./node");
|
|
5
5
|
const validate = require("../validate");
|
|
6
6
|
const error_1 = require("../../../error");
|
|
7
|
-
const RUNTIMES = ["nodejs10", "nodejs12", "nodejs14", "nodejs16"];
|
|
7
|
+
const RUNTIMES = ["nodejs10", "nodejs12", "nodejs14", "nodejs16", "nodejs18"];
|
|
8
8
|
const EXPERIMENTAL_RUNTIMES = ["go113"];
|
|
9
9
|
const DEPRECATED_RUNTIMES = ["nodejs6", "nodejs8"];
|
|
10
10
|
function isDeprecatedRuntime(runtime) {
|
|
@@ -22,6 +22,7 @@ const MESSAGE_FRIENDLY_RUNTIMES = {
|
|
|
22
22
|
nodejs12: "Node.js 12",
|
|
23
23
|
nodejs14: "Node.js 14",
|
|
24
24
|
nodejs16: "Node.js 16",
|
|
25
|
+
nodejs18: "Node.js 18",
|
|
25
26
|
go113: "Go 1.13",
|
|
26
27
|
};
|
|
27
28
|
function getHumanFriendlyRuntimeName(runtime) {
|
|
@@ -38,10 +38,10 @@ class Delegate {
|
|
|
38
38
|
this.sourceDir = sourceDir;
|
|
39
39
|
this.runtime = runtime;
|
|
40
40
|
this.name = "nodejs";
|
|
41
|
-
this._sdkVersion =
|
|
41
|
+
this._sdkVersion = undefined;
|
|
42
42
|
}
|
|
43
43
|
get sdkVersion() {
|
|
44
|
-
if (
|
|
44
|
+
if (this._sdkVersion === undefined) {
|
|
45
45
|
this._sdkVersion = versioning.getFunctionsSDKVersion(this.sourceDir) || "";
|
|
46
46
|
}
|
|
47
47
|
return this._sdkVersion;
|
|
@@ -14,6 +14,7 @@ const ENGINE_RUNTIMES = {
|
|
|
14
14
|
12: "nodejs12",
|
|
15
15
|
14: "nodejs14",
|
|
16
16
|
16: "nodejs16",
|
|
17
|
+
18: "nodejs18",
|
|
17
18
|
};
|
|
18
19
|
const ENGINE_RUNTIMES_NAMES = Object.values(ENGINE_RUNTIMES);
|
|
19
20
|
exports.RUNTIME_NOT_SET = "`runtime` field is required but was not found in firebase.json.\n" +
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.checkFunctionsSDKVersion = exports.getLatestSDKVersion = exports.getFunctionsSDKVersion = exports.FUNCTIONS_SDK_VERSION_TOO_OLD_WARNING = void 0;
|
|
4
|
-
const
|
|
3
|
+
exports.checkFunctionsSDKVersion = exports.getLatestSDKVersion = exports.getFunctionsSDKVersion = exports.findModuleVersion = exports.FUNCTIONS_SDK_VERSION_TOO_OLD_WARNING = void 0;
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
5
6
|
const clc = require("colorette");
|
|
6
7
|
const semver = require("semver");
|
|
7
8
|
const spawn = require("cross-spawn");
|
|
@@ -15,26 +16,41 @@ exports.FUNCTIONS_SDK_VERSION_TOO_OLD_WARNING = clc.bold(clc.yellow("functions:
|
|
|
15
16
|
" version that is at least 2.0.0. Please run " +
|
|
16
17
|
clc.bold("npm i --save firebase-functions@latest") +
|
|
17
18
|
" in the functions folder.";
|
|
18
|
-
function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
});
|
|
24
|
-
if (child.error) {
|
|
25
|
-
logger_1.logger.debug("getFunctionsSDKVersion encountered error:", child.error.stack);
|
|
19
|
+
function findModuleVersion(name, resolvedPath) {
|
|
20
|
+
let searchPath = path.dirname(resolvedPath);
|
|
21
|
+
while (true) {
|
|
22
|
+
if (searchPath === "/" || path.basename(searchPath) === "node_modules") {
|
|
23
|
+
logger_1.logger.debug(`Failed to find version of module ${name}: reached end of search path ${searchPath}`);
|
|
26
24
|
return;
|
|
27
25
|
}
|
|
28
|
-
const
|
|
29
|
-
|
|
26
|
+
const maybePackageJson = path.join(searchPath, "package.json");
|
|
27
|
+
if (fs.existsSync(maybePackageJson)) {
|
|
28
|
+
const pkg = require(maybePackageJson);
|
|
29
|
+
if (pkg.name === name) {
|
|
30
|
+
return pkg.version;
|
|
31
|
+
}
|
|
32
|
+
logger_1.logger.debug(`Failed to find version of module ${name}: instead found ${pkg.name} at ${searchPath}`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
searchPath = path.dirname(searchPath);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.findModuleVersion = findModuleVersion;
|
|
39
|
+
function getFunctionsSDKVersion(sourceDir) {
|
|
40
|
+
try {
|
|
41
|
+
return findModuleVersion("firebase-functions", require.resolve("firebase-functions", { paths: [sourceDir] }));
|
|
30
42
|
}
|
|
31
43
|
catch (e) {
|
|
44
|
+
if (e.code === "MODULE_NOT_FOUND") {
|
|
45
|
+
utils.logLabeledWarning("functions", "Couldn't find firebase-functions package in your source code. Have you run 'npm install'?");
|
|
46
|
+
}
|
|
32
47
|
logger_1.logger.debug("getFunctionsSDKVersion encountered error:", e);
|
|
33
48
|
return;
|
|
34
49
|
}
|
|
35
50
|
}
|
|
36
51
|
exports.getFunctionsSDKVersion = getFunctionsSDKVersion;
|
|
37
52
|
function getLatestSDKVersion() {
|
|
53
|
+
var _a;
|
|
38
54
|
const child = spawn.sync("npm", ["show", "firebase-functions", "--json=true"], {
|
|
39
55
|
encoding: "utf8",
|
|
40
56
|
});
|
|
@@ -43,10 +59,10 @@ function getLatestSDKVersion() {
|
|
|
43
59
|
return;
|
|
44
60
|
}
|
|
45
61
|
const output = JSON.parse(child.stdout);
|
|
46
|
-
if (
|
|
62
|
+
if (Object.keys(output).length === 0) {
|
|
47
63
|
return;
|
|
48
64
|
}
|
|
49
|
-
return
|
|
65
|
+
return (_a = output["dist-tags"]) === null || _a === void 0 ? void 0 : _a["latest"];
|
|
50
66
|
}
|
|
51
67
|
exports.getLatestSDKVersion = getLatestSDKVersion;
|
|
52
68
|
function checkFunctionsSDKVersion(currentVersion) {
|
|
@@ -219,7 +219,8 @@ exports.shutdownWhenKilled = shutdownWhenKilled;
|
|
|
219
219
|
async function runScript(script, extraEnv) {
|
|
220
220
|
utils.logBullet(`Running script: ${clc.bold(script)}`);
|
|
221
221
|
const env = Object.assign(Object.assign({}, process.env), extraEnv);
|
|
222
|
-
|
|
222
|
+
const emulatorInfos = registry_1.EmulatorRegistry.listRunningWithInfo();
|
|
223
|
+
(0, env_1.setEnvVarsForEmulators)(env, emulatorInfos);
|
|
223
224
|
const proc = childProcess.spawn(script, {
|
|
224
225
|
stdio: ["inherit", "inherit", "inherit"],
|
|
225
226
|
shell: true,
|
|
@@ -28,9 +28,9 @@ const EMULATOR_UPDATE_DETAILS = {
|
|
|
28
28
|
expectedChecksum: "4f41d24a3c0f3b55ea22804a424cc0ee",
|
|
29
29
|
},
|
|
30
30
|
storage: {
|
|
31
|
-
version: "1.1.
|
|
32
|
-
expectedSize:
|
|
33
|
-
expectedChecksum: "
|
|
31
|
+
version: "1.1.2",
|
|
32
|
+
expectedSize: 47028740,
|
|
33
|
+
expectedChecksum: "983b4415b1e72b109864f1b8e7ea7546",
|
|
34
34
|
},
|
|
35
35
|
ui: experiments.isEnabled("emulatoruisnapshot")
|
|
36
36
|
? { version: "SNAPSHOT", expectedSize: -1, expectedChecksum: "" }
|
package/lib/emulator/env.js
CHANGED
|
@@ -3,33 +3,35 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.setEnvVarsForEmulators = void 0;
|
|
4
4
|
const constants_1 = require("./constants");
|
|
5
5
|
const types_1 = require("./types");
|
|
6
|
-
const
|
|
7
|
-
function setEnvVarsForEmulators(env) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
6
|
+
const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
|
|
7
|
+
function setEnvVarsForEmulators(env, emulators) {
|
|
8
|
+
for (const emu of emulators) {
|
|
9
|
+
const host = (0, functionsEmulatorShared_1.formatHost)(emu);
|
|
10
|
+
switch (emu.name) {
|
|
11
|
+
case types_1.Emulators.FIRESTORE:
|
|
12
|
+
env[constants_1.Constants.FIRESTORE_EMULATOR_HOST] = host;
|
|
13
|
+
env[constants_1.Constants.FIRESTORE_EMULATOR_ENV_ALT] = host;
|
|
14
|
+
break;
|
|
15
|
+
case types_1.Emulators.DATABASE:
|
|
16
|
+
env[constants_1.Constants.FIREBASE_DATABASE_EMULATOR_HOST] = host;
|
|
17
|
+
break;
|
|
18
|
+
case types_1.Emulators.STORAGE:
|
|
19
|
+
env[constants_1.Constants.FIREBASE_STORAGE_EMULATOR_HOST] = host;
|
|
20
|
+
env[constants_1.Constants.CLOUD_STORAGE_EMULATOR_HOST] = `http://${host}`;
|
|
21
|
+
break;
|
|
22
|
+
case types_1.Emulators.AUTH:
|
|
23
|
+
env[constants_1.Constants.FIREBASE_AUTH_EMULATOR_HOST] = host;
|
|
24
|
+
break;
|
|
25
|
+
case types_1.Emulators.HUB:
|
|
26
|
+
env[constants_1.Constants.FIREBASE_EMULATOR_HUB] = host;
|
|
27
|
+
break;
|
|
28
|
+
case types_1.Emulators.PUBSUB:
|
|
29
|
+
env[constants_1.Constants.PUBSUB_EMULATOR_HOST] = host;
|
|
30
|
+
break;
|
|
31
|
+
case types_1.Emulators.EVENTARC:
|
|
32
|
+
env[constants_1.Constants.CLOUD_EVENTARC_EMULATOR_HOST] = `http://${host}`;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
exports.setEnvVarsForEmulators = setEnvVarsForEmulators;
|
|
@@ -59,6 +59,8 @@ function checkForUnemulatedTriggerTypes(backend, options) {
|
|
|
59
59
|
return !(0, controller_1.shouldStart)(options, types_1.Emulators.AUTH);
|
|
60
60
|
case constants_1.Constants.SERVICE_STORAGE:
|
|
61
61
|
return !(0, controller_1.shouldStart)(options, types_1.Emulators.STORAGE);
|
|
62
|
+
case constants_1.Constants.SERVICE_EVENTARC:
|
|
63
|
+
return !(0, controller_1.shouldStart)(options, types_1.Emulators.EVENTARC);
|
|
62
64
|
default:
|
|
63
65
|
return true;
|
|
64
66
|
}
|
|
@@ -142,7 +142,7 @@ class ExtensionsEmulator {
|
|
|
142
142
|
const emulatableBackend = {
|
|
143
143
|
functionsDir,
|
|
144
144
|
env: nonSecretEnv,
|
|
145
|
-
codebase:
|
|
145
|
+
codebase: instance.instanceId,
|
|
146
146
|
secretEnv: secretEnvVariables,
|
|
147
147
|
predefinedTriggers: extensionTriggers,
|
|
148
148
|
nodeMajorVersion: nodeMajorVersion,
|
|
@@ -107,9 +107,11 @@ class FunctionsEmulator {
|
|
|
107
107
|
const httpsFunctionRoutes = [httpsFunctionRoute, `${httpsFunctionRoute}/*`];
|
|
108
108
|
const listBackendsRoute = `/backends`;
|
|
109
109
|
const httpsHandler = (req, res) => {
|
|
110
|
-
|
|
110
|
+
const work = () => {
|
|
111
111
|
return this.handleHttpsTrigger(req, res);
|
|
112
|
-
}
|
|
112
|
+
};
|
|
113
|
+
work.type = `${req.path}-${new Date().toISOString()}`;
|
|
114
|
+
this.workQueue.submit(work);
|
|
113
115
|
};
|
|
114
116
|
const multicastHandler = (req, res) => {
|
|
115
117
|
var _a;
|
|
@@ -129,7 +131,7 @@ class FunctionsEmulator {
|
|
|
129
131
|
const triggers = this.multicastTriggers[triggerKey] || [];
|
|
130
132
|
const { host, port } = this.getInfo();
|
|
131
133
|
triggers.forEach((triggerId) => {
|
|
132
|
-
|
|
134
|
+
const work = () => {
|
|
133
135
|
return new Promise((resolve, reject) => {
|
|
134
136
|
const trigReq = http.request({
|
|
135
137
|
host: (0, utils_1.connectableHostname)(host),
|
|
@@ -143,7 +145,9 @@ class FunctionsEmulator {
|
|
|
143
145
|
trigReq.end();
|
|
144
146
|
resolve();
|
|
145
147
|
});
|
|
146
|
-
}
|
|
148
|
+
};
|
|
149
|
+
work.type = `${triggerId}-${new Date().toISOString()}`;
|
|
150
|
+
this.workQueue.submit(work);
|
|
147
151
|
});
|
|
148
152
|
res.json({ status: "multicast_acknowledged" });
|
|
149
153
|
};
|
|
@@ -262,7 +266,6 @@ class FunctionsEmulator {
|
|
|
262
266
|
await runtimeDelegate.validate();
|
|
263
267
|
logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
|
|
264
268
|
await runtimeDelegate.build();
|
|
265
|
-
logger_1.logger.debug(`Analyzing ${runtimeDelegate.name} backend spec`);
|
|
266
269
|
const firebaseConfig = this.getFirebaseConfig();
|
|
267
270
|
const environment = Object.assign(Object.assign(Object.assign(Object.assign({}, this.getSystemEnvs()), this.getEmulatorEnvs()), { FIREBASE_CONFIG: firebaseConfig }), emulatableBackend.env);
|
|
268
271
|
const userEnvOpt = {
|
|
@@ -270,8 +273,9 @@ class FunctionsEmulator {
|
|
|
270
273
|
projectId: this.args.projectId,
|
|
271
274
|
projectAlias: this.args.projectAlias,
|
|
272
275
|
};
|
|
276
|
+
const userEnvs = functionsEnv.loadUserEnvs(userEnvOpt);
|
|
273
277
|
const discoveredBuild = await runtimeDelegate.discoverBuild(runtimeConfig, environment);
|
|
274
|
-
const resolution = await (0, build_1.resolveBackend)(discoveredBuild, JSON.parse(firebaseConfig), userEnvOpt,
|
|
278
|
+
const resolution = await (0, build_1.resolveBackend)(discoveredBuild, JSON.parse(firebaseConfig), userEnvOpt, userEnvs);
|
|
275
279
|
const discoveredBackend = resolution.backend;
|
|
276
280
|
const endpoints = backend.allEndpoints(discoveredBackend);
|
|
277
281
|
(0, functionsEmulatorShared_1.prepareEndpoints)(endpoints);
|
|
@@ -377,6 +381,12 @@ class FunctionsEmulator {
|
|
|
377
381
|
}
|
|
378
382
|
}
|
|
379
383
|
if (this.args.debugPort) {
|
|
384
|
+
emulatableBackend.secretEnv = Object.values(toSetup.reduce((acc, curr) => {
|
|
385
|
+
for (const secret of curr.secretEnvironmentVariables || []) {
|
|
386
|
+
acc[secret.key] = secret;
|
|
387
|
+
}
|
|
388
|
+
return acc;
|
|
389
|
+
}, {}));
|
|
380
390
|
await this.startRuntime(emulatableBackend);
|
|
381
391
|
}
|
|
382
392
|
}
|
|
@@ -711,7 +721,11 @@ class FunctionsEmulator {
|
|
|
711
721
|
skipTokenVerification: true,
|
|
712
722
|
enableCors: true,
|
|
713
723
|
});
|
|
714
|
-
|
|
724
|
+
let emulatorInfos = registry_1.EmulatorRegistry.listRunningWithInfo();
|
|
725
|
+
if (this.args.remoteEmulators) {
|
|
726
|
+
emulatorInfos = emulatorInfos.concat(Object.values(this.args.remoteEmulators));
|
|
727
|
+
}
|
|
728
|
+
(0, env_1.setEnvVarsForEmulators)(envs, emulatorInfos);
|
|
715
729
|
if (this.args.debugPort) {
|
|
716
730
|
envs["FUNCTION_DEBUG_MODE"] = "true";
|
|
717
731
|
}
|
|
@@ -749,34 +763,32 @@ class FunctionsEmulator {
|
|
|
749
763
|
this.logger.logLabeled("ERROR", "functions", `Failed to read local secrets file ${secretPath}: ${e.message}`);
|
|
750
764
|
}
|
|
751
765
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
errs.push(result.reason);
|
|
767
|
-
}
|
|
768
|
-
else {
|
|
769
|
-
const [k, v] = result.value;
|
|
770
|
-
secretEnvs[k] = v;
|
|
771
|
-
}
|
|
766
|
+
const secrets = (trigger === null || trigger === void 0 ? void 0 : trigger.secretEnvironmentVariables) || backend.secretEnv;
|
|
767
|
+
const accesses = secrets
|
|
768
|
+
.filter((s) => !secretEnvs[s.key])
|
|
769
|
+
.map(async (s) => {
|
|
770
|
+
var _a;
|
|
771
|
+
this.logger.logLabeled("INFO", "functions", `Trying to access secret ${s.secret}@latest`);
|
|
772
|
+
const value = await (0, secretManager_1.accessSecretVersion)(this.getProjectId(), s.secret, (_a = s.version) !== null && _a !== void 0 ? _a : "latest");
|
|
773
|
+
return [s.key, value];
|
|
774
|
+
});
|
|
775
|
+
const accessResults = await (0, utils_1.allSettled)(accesses);
|
|
776
|
+
const errs = [];
|
|
777
|
+
for (const result of accessResults) {
|
|
778
|
+
if (result.status === "rejected") {
|
|
779
|
+
errs.push(result.reason);
|
|
772
780
|
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
`or provide override values in ${secretPath}:\n\t` +
|
|
777
|
-
errs.join("\n\t"));
|
|
781
|
+
else {
|
|
782
|
+
const [k, v] = result.value;
|
|
783
|
+
secretEnvs[k] = v;
|
|
778
784
|
}
|
|
779
785
|
}
|
|
786
|
+
if (errs.length > 0) {
|
|
787
|
+
this.logger.logLabeled("ERROR", "functions", "Unable to access secret environment variables from Google Cloud Secret Manager. " +
|
|
788
|
+
"Make sure the credential used for the Functions Emulator have access " +
|
|
789
|
+
`or provide override values in ${secretPath}:\n\t` +
|
|
790
|
+
errs.join("\n\t"));
|
|
791
|
+
}
|
|
780
792
|
return secretEnvs;
|
|
781
793
|
}
|
|
782
794
|
async startRuntime(backend, trigger) {
|
|
@@ -283,7 +283,7 @@ function toExpressionValue(obj) {
|
|
|
283
283
|
}
|
|
284
284
|
if (obj == null) {
|
|
285
285
|
return {
|
|
286
|
-
null_value:
|
|
286
|
+
null_value: null,
|
|
287
287
|
};
|
|
288
288
|
}
|
|
289
289
|
if (typeof obj === "object") {
|
|
@@ -339,7 +339,7 @@ function createRequestExpressionValue(opts) {
|
|
|
339
339
|
},
|
|
340
340
|
time: toExpressionValue(new Date()),
|
|
341
341
|
resource: toExpressionValue(opts.file.after ? opts.file.after : null),
|
|
342
|
-
auth: opts.token ? createAuthExpressionValue(opts) : { null_value:
|
|
342
|
+
auth: opts.token ? createAuthExpressionValue(opts) : { null_value: null },
|
|
343
343
|
};
|
|
344
344
|
return {
|
|
345
345
|
map_value: {
|
|
@@ -11,7 +11,7 @@ class WorkQueue {
|
|
|
11
11
|
this.maxParallelWork = maxParallelWork;
|
|
12
12
|
this.logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS);
|
|
13
13
|
this.queue = [];
|
|
14
|
-
this.
|
|
14
|
+
this.running = [];
|
|
15
15
|
this.notifyQueue = () => {
|
|
16
16
|
};
|
|
17
17
|
this.notifyWorkFinish = () => {
|
|
@@ -37,8 +37,8 @@ class WorkQueue {
|
|
|
37
37
|
this.notifyQueue = res;
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
|
-
if (this.
|
|
41
|
-
this.logger.logLabeled("DEBUG", "work-queue", `waiting for work to finish (running=${this.
|
|
40
|
+
if (this.running.length >= this.maxParallelWork) {
|
|
41
|
+
this.logger.logLabeled("DEBUG", "work-queue", `waiting for work to finish (running=${this.running})`);
|
|
42
42
|
await new Promise((res) => {
|
|
43
43
|
this.notifyWorkFinish = res;
|
|
44
44
|
});
|
|
@@ -74,8 +74,10 @@ class WorkQueue {
|
|
|
74
74
|
}
|
|
75
75
|
getState() {
|
|
76
76
|
return {
|
|
77
|
+
queuedWork: this.queue.map((work) => work.type),
|
|
77
78
|
queueLength: this.queue.length,
|
|
78
|
-
|
|
79
|
+
runningWork: this.running,
|
|
80
|
+
workRunningCount: this.running.length,
|
|
79
81
|
};
|
|
80
82
|
}
|
|
81
83
|
isWorking() {
|
|
@@ -85,7 +87,7 @@ class WorkQueue {
|
|
|
85
87
|
async runNext() {
|
|
86
88
|
const next = this.queue.shift();
|
|
87
89
|
if (next) {
|
|
88
|
-
this.
|
|
90
|
+
this.running.push(next.type || "anonymous");
|
|
89
91
|
this.logState();
|
|
90
92
|
try {
|
|
91
93
|
await next();
|
|
@@ -94,7 +96,10 @@ class WorkQueue {
|
|
|
94
96
|
this.logger.log("DEBUG", e);
|
|
95
97
|
}
|
|
96
98
|
finally {
|
|
97
|
-
this.
|
|
99
|
+
const index = this.running.indexOf(next.type || "anonymous");
|
|
100
|
+
if (index !== -1) {
|
|
101
|
+
this.running.splice(index, 1);
|
|
102
|
+
}
|
|
98
103
|
this.notifyWorkFinish();
|
|
99
104
|
this.logState();
|
|
100
105
|
}
|