firebase-tools 11.20.0 → 11.22.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/bin/firebase.js +1 -1
- package/lib/commands/appdistribution-distribute.js +3 -0
- package/lib/commands/ext-configure.js +2 -1
- package/lib/commands/ext-install.js +2 -2
- package/lib/deploy/extensions/planner.js +16 -9
- package/lib/deploy/extensions/tasks.js +6 -0
- package/lib/deploy/functions/prepareFunctionsUpload.js +13 -2
- package/lib/deploy/functions/runtimes/discovery/index.js +1 -1
- package/lib/deploy/functions/runtimes/index.js +5 -3
- package/lib/deploy/functions/runtimes/node/index.js +71 -26
- package/lib/deploy/functions/runtimes/node/versioning.js +4 -2
- package/lib/deploy/functions/runtimes/python/index.js +119 -0
- package/lib/emulator/auth/apiSpec.js +4 -0
- package/lib/emulator/auth/operations.js +3 -3
- package/lib/emulator/controller.js +5 -5
- package/lib/emulator/downloadableEmulators.js +3 -3
- package/lib/emulator/extensionsEmulator.js +3 -2
- package/lib/emulator/functionsEmulator.js +107 -72
- package/lib/emulator/functionsRuntimeWorker.js +4 -14
- package/lib/emulator/storage/apis/firebase.js +1 -0
- package/lib/emulator/storage/rules/manager.js +1 -1
- package/lib/emulator/storage/server.js +1 -1
- package/lib/emulator/storage/upload.js +3 -3
- package/lib/extensions/askUserForParam.js +32 -1
- package/lib/extensions/emulator/optionsHelper.js +3 -3
- package/lib/extensions/emulator/specHelper.js +17 -16
- package/lib/extensions/extensionsApi.js +16 -3
- package/lib/extensions/paramHelper.js +6 -1
- package/lib/firestore/checkDatabaseType.js +3 -3
- package/lib/frameworks/index.js +9 -8
- package/lib/frameworks/next/index.js +6 -4
- package/lib/frameworks/nuxt/index.js +18 -26
- package/lib/frameworks/nuxt/interfaces.js +2 -0
- package/lib/frameworks/nuxt/utils.js +13 -0
- package/lib/frameworks/nuxt2/index.js +91 -0
- package/lib/functional.js +10 -3
- package/lib/functions/env.js +1 -1
- package/lib/functions/python.js +21 -0
- package/lib/init/features/firestore/index.js +1 -3
- package/lib/serve/functions.js +1 -3
- package/npm-shrinkwrap.json +1805 -1171
- package/package.json +3 -3
- package/lib/deploy/functions/runtimes/golang/gomod.js +0 -74
- package/lib/deploy/functions/runtimes/golang/index.js +0 -106
- package/lib/init/features/functions/golang.js +0 -57
- package/templates/init/functions/golang/_gitignore +0 -12
- package/templates/init/functions/golang/functions.go +0 -38
package/lib/bin/firebase.js
CHANGED
|
@@ -8,7 +8,7 @@ if (!semver.satisfies(nodeVersion, pkg.engines.node)) {
|
|
|
8
8
|
console.error(`Firebase CLI v${pkg.version} is incompatible with Node.js ${nodeVersion} Please upgrade Node.js to version ${pkg.engines.node}`);
|
|
9
9
|
process.exit(1);
|
|
10
10
|
}
|
|
11
|
-
const updateNotifierPkg = require("update-notifier");
|
|
11
|
+
const updateNotifierPkg = require("update-notifier-cjs");
|
|
12
12
|
const clc = require("colorette");
|
|
13
13
|
const TerminalRenderer = require("marked-terminal");
|
|
14
14
|
const updateNotifier = updateNotifierPkg({ pkg });
|
|
@@ -90,6 +90,9 @@ exports.command = new command_1.Command("appdistribution:distribute <release-bin
|
|
|
90
90
|
default:
|
|
91
91
|
utils.logSuccess(`uploaded release ${release.displayVersion} (${release.buildVersion}) successfully!`);
|
|
92
92
|
}
|
|
93
|
+
utils.logSuccess(`View this release in the Firebase console: ${release.firebaseConsoleUri}`);
|
|
94
|
+
utils.logSuccess(`Share this release with testers who have access: ${release.testingUri}`);
|
|
95
|
+
utils.logSuccess(`Download the release binary (link expires in 1 hour): ${release.binaryDownloadUri}`);
|
|
93
96
|
releaseName = uploadResponse.release.name;
|
|
94
97
|
}
|
|
95
98
|
catch (err) {
|
|
@@ -32,6 +32,7 @@ exports.command = new command_1.Command("ext:configure <extensionInstanceId>")
|
|
|
32
32
|
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
|
|
33
33
|
.before(extensionsHelper_1.diagnoseAndFixProject)
|
|
34
34
|
.action(async (instanceId, options) => {
|
|
35
|
+
var _a;
|
|
35
36
|
const projectId = (0, projectUtils_1.getProjectId)(options);
|
|
36
37
|
if (options.nonInteractive) {
|
|
37
38
|
throw new error_1.FirebaseError(`Command not supported in non-interactive mode, edit ./extensions/${instanceId}.env directly instead. ` +
|
|
@@ -56,7 +57,7 @@ exports.command = new command_1.Command("ext:configure <extensionInstanceId>")
|
|
|
56
57
|
instanceId,
|
|
57
58
|
projectDir: config.projectDir,
|
|
58
59
|
});
|
|
59
|
-
const [immutableParams, tbdParams] = (0, functional_1.partition)(spec.params, (param) => { var _a; return (_a = param.immutable) !== null && _a !== void 0 ? _a : false; });
|
|
60
|
+
const [immutableParams, tbdParams] = (0, functional_1.partition)(spec.params.concat((_a = spec.systemParams) !== null && _a !== void 0 ? _a : []), (param) => { var _a; return (_a = param.immutable) !== null && _a !== void 0 ? _a : false; });
|
|
60
61
|
infoImmutableParams(immutableParams, oldParamValues);
|
|
61
62
|
paramHelper.setNewDefaults(tbdParams, oldParamValues);
|
|
62
63
|
const mutableParamsBindingOptions = await paramHelper.getParams({
|
|
@@ -120,7 +120,7 @@ async function infoExtensionVersion(args) {
|
|
|
120
120
|
await (0, warnings_1.displayWarningPrompts)(ref.publisherId, extension.registryLaunchStage, args.extensionVersion);
|
|
121
121
|
}
|
|
122
122
|
async function installToManifest(options) {
|
|
123
|
-
var _a;
|
|
123
|
+
var _a, _b;
|
|
124
124
|
const { projectId, extensionName, extVersion, source, paramsEnvPath, nonInteractive, force } = options;
|
|
125
125
|
const isLocalSource = (0, extensionsHelper_1.isLocalPath)(extensionName);
|
|
126
126
|
const spec = (_a = extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec) !== null && _a !== void 0 ? _a : source === null || source === void 0 ? void 0 : source.spec;
|
|
@@ -134,7 +134,7 @@ async function installToManifest(options) {
|
|
|
134
134
|
}
|
|
135
135
|
const paramBindingOptions = await paramHelper.getParams({
|
|
136
136
|
projectId,
|
|
137
|
-
paramSpecs: spec.params,
|
|
137
|
+
paramSpecs: spec.params.concat((_b = spec.systemParams) !== null && _b !== void 0 ? _b : []),
|
|
138
138
|
nonInteractive,
|
|
139
139
|
paramsEnvPath,
|
|
140
140
|
instanceId,
|
|
@@ -8,7 +8,9 @@ const error_1 = require("../../error");
|
|
|
8
8
|
const extensionsHelper_1 = require("../../extensions/extensionsHelper");
|
|
9
9
|
const logger_1 = require("../../logger");
|
|
10
10
|
const manifest_1 = require("../../extensions/manifest");
|
|
11
|
+
const paramHelper_1 = require("../../extensions/paramHelper");
|
|
11
12
|
const specHelper_1 = require("../../extensions/emulator/specHelper");
|
|
13
|
+
const functional_1 = require("../../functional");
|
|
12
14
|
async function getExtensionVersion(i) {
|
|
13
15
|
if (!i.extensionVersion) {
|
|
14
16
|
if (!i.ref) {
|
|
@@ -49,9 +51,11 @@ exports.getExtensionSpec = getExtensionSpec;
|
|
|
49
51
|
async function have(projectId) {
|
|
50
52
|
const instances = await extensionsApi.listInstances(projectId);
|
|
51
53
|
return instances.map((i) => {
|
|
54
|
+
var _a;
|
|
52
55
|
const dep = {
|
|
53
56
|
instanceId: i.name.split("/").pop(),
|
|
54
57
|
params: i.config.params,
|
|
58
|
+
systemParams: (_a = i.config.systemParams) !== null && _a !== void 0 ? _a : {},
|
|
55
59
|
allowedEventTypes: i.config.allowedEventTypes,
|
|
56
60
|
eventarcChannel: i.config.eventarcChannel,
|
|
57
61
|
etag: i.etag,
|
|
@@ -71,7 +75,7 @@ async function want(args) {
|
|
|
71
75
|
for (const e of Object.entries(args.extensions)) {
|
|
72
76
|
try {
|
|
73
77
|
const instanceId = e[0];
|
|
74
|
-
const
|
|
78
|
+
const rawParams = (0, manifest_1.readInstanceParam)({
|
|
75
79
|
projectDir: args.projectDir,
|
|
76
80
|
instanceId,
|
|
77
81
|
projectId: args.projectId,
|
|
@@ -80,18 +84,20 @@ async function want(args) {
|
|
|
80
84
|
checkLocal: args.emulatorMode,
|
|
81
85
|
});
|
|
82
86
|
const autoPopulatedParams = await (0, extensionsHelper_1.getFirebaseProjectParams)(args.projectId, args.emulatorMode);
|
|
83
|
-
const subbedParams = (0, extensionsHelper_1.substituteParams)(
|
|
84
|
-
const
|
|
85
|
-
|
|
87
|
+
const subbedParams = (0, extensionsHelper_1.substituteParams)(rawParams, autoPopulatedParams);
|
|
88
|
+
const [systemParams, params] = (0, functional_1.partitionRecord)(subbedParams, paramHelper_1.isSystemParam);
|
|
89
|
+
const allowedEventTypes = params.ALLOWED_EVENT_TYPES !== undefined
|
|
90
|
+
? params.ALLOWED_EVENT_TYPES.split(",").filter((e) => e !== "")
|
|
86
91
|
: undefined;
|
|
87
|
-
const eventarcChannel =
|
|
88
|
-
delete
|
|
89
|
-
delete
|
|
92
|
+
const eventarcChannel = params.EVENTARC_CHANNEL;
|
|
93
|
+
delete params["EVENTARC_CHANNEL"];
|
|
94
|
+
delete params["ALLOWED_EVENT_TYPES"];
|
|
90
95
|
if ((0, extensionsHelper_1.isLocalPath)(e[1])) {
|
|
91
96
|
instanceSpecs.push({
|
|
92
97
|
instanceId,
|
|
93
98
|
localPath: e[1],
|
|
94
|
-
params
|
|
99
|
+
params,
|
|
100
|
+
systemParams,
|
|
95
101
|
allowedEventTypes: allowedEventTypes,
|
|
96
102
|
eventarcChannel: eventarcChannel,
|
|
97
103
|
});
|
|
@@ -102,7 +108,8 @@ async function want(args) {
|
|
|
102
108
|
instanceSpecs.push({
|
|
103
109
|
instanceId,
|
|
104
110
|
ref,
|
|
105
|
-
params
|
|
111
|
+
params,
|
|
112
|
+
systemParams,
|
|
106
113
|
allowedEventTypes: allowedEventTypes,
|
|
107
114
|
eventarcChannel: eventarcChannel,
|
|
108
115
|
});
|
|
@@ -32,6 +32,7 @@ function createExtensionInstanceTask(projectId, instanceSpec, validateOnly = fal
|
|
|
32
32
|
projectId,
|
|
33
33
|
instanceId: instanceSpec.instanceId,
|
|
34
34
|
params: instanceSpec.params,
|
|
35
|
+
systemParams: instanceSpec.systemParams,
|
|
35
36
|
extensionVersionRef: refs.toExtensionVersionRef(instanceSpec.ref),
|
|
36
37
|
allowedEventTypes: instanceSpec.allowedEventTypes,
|
|
37
38
|
eventarcChannel: instanceSpec.eventarcChannel,
|
|
@@ -44,6 +45,7 @@ function createExtensionInstanceTask(projectId, instanceSpec, validateOnly = fal
|
|
|
44
45
|
projectId,
|
|
45
46
|
instanceId: instanceSpec.instanceId,
|
|
46
47
|
params: instanceSpec.params,
|
|
48
|
+
systemParams: instanceSpec.systemParams,
|
|
47
49
|
extensionSource,
|
|
48
50
|
allowedEventTypes: instanceSpec.allowedEventTypes,
|
|
49
51
|
eventarcChannel: instanceSpec.eventarcChannel,
|
|
@@ -71,6 +73,7 @@ function updateExtensionInstanceTask(projectId, instanceSpec, validateOnly = fal
|
|
|
71
73
|
instanceId: instanceSpec.instanceId,
|
|
72
74
|
extRef: refs.toExtensionVersionRef(instanceSpec.ref),
|
|
73
75
|
params: instanceSpec.params,
|
|
76
|
+
systemParams: instanceSpec.systemParams,
|
|
74
77
|
canEmitEvents: !!instanceSpec.allowedEventTypes,
|
|
75
78
|
allowedEventTypes: instanceSpec.allowedEventTypes,
|
|
76
79
|
eventarcChannel: instanceSpec.eventarcChannel,
|
|
@@ -84,6 +87,8 @@ function updateExtensionInstanceTask(projectId, instanceSpec, validateOnly = fal
|
|
|
84
87
|
instanceId: instanceSpec.instanceId,
|
|
85
88
|
extensionSource,
|
|
86
89
|
validateOnly,
|
|
90
|
+
params: instanceSpec.params,
|
|
91
|
+
systemParams: instanceSpec.systemParams,
|
|
87
92
|
canEmitEvents: !!instanceSpec.allowedEventTypes,
|
|
88
93
|
allowedEventTypes: instanceSpec.allowedEventTypes,
|
|
89
94
|
eventarcChannel: instanceSpec.eventarcChannel,
|
|
@@ -109,6 +114,7 @@ function configureExtensionInstanceTask(projectId, instanceSpec, validateOnly =
|
|
|
109
114
|
projectId,
|
|
110
115
|
instanceId: instanceSpec.instanceId,
|
|
111
116
|
params: instanceSpec.params,
|
|
117
|
+
systemParams: instanceSpec.systemParams,
|
|
112
118
|
canEmitEvents: !!instanceSpec.allowedEventTypes,
|
|
113
119
|
allowedEventTypes: instanceSpec.allowedEventTypes,
|
|
114
120
|
eventarcChannel: instanceSpec.eventarcChannel,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.prepareFunctionsUpload = exports.getFunctionsConfig = void 0;
|
|
3
|
+
exports.convertToSortedKeyValueArray = exports.prepareFunctionsUpload = exports.getFunctionsConfig = void 0;
|
|
4
4
|
const archiver = require("archiver");
|
|
5
5
|
const clc = require("colorette");
|
|
6
6
|
const filesize = require("filesize");
|
|
@@ -66,8 +66,9 @@ async function packageSource(sourceDir, config, runtimeConfig) {
|
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
if (typeof runtimeConfig !== "undefined") {
|
|
69
|
+
const runtimeConfigHashString = JSON.stringify(convertToSortedKeyValueArray(runtimeConfig));
|
|
70
|
+
hashes.push(runtimeConfigHashString);
|
|
69
71
|
const runtimeConfigString = JSON.stringify(runtimeConfig, null, 2);
|
|
70
|
-
hashes.push(runtimeConfigString);
|
|
71
72
|
archive.append(runtimeConfigString, {
|
|
72
73
|
name: CONFIG_DEST_FILE,
|
|
73
74
|
mode: 420,
|
|
@@ -94,3 +95,13 @@ async function prepareFunctionsUpload(sourceDir, config, runtimeConfig) {
|
|
|
94
95
|
return packageSource(sourceDir, config, runtimeConfig);
|
|
95
96
|
}
|
|
96
97
|
exports.prepareFunctionsUpload = prepareFunctionsUpload;
|
|
98
|
+
function convertToSortedKeyValueArray(config) {
|
|
99
|
+
if (typeof config !== "object" || config === null)
|
|
100
|
+
return config;
|
|
101
|
+
return Object.keys(config)
|
|
102
|
+
.sort()
|
|
103
|
+
.map((key) => {
|
|
104
|
+
return { key, value: convertToSortedKeyValueArray(config[key]) };
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
exports.convertToSortedKeyValueArray = convertToSortedKeyValueArray;
|
|
@@ -54,7 +54,7 @@ async function detectFromPort(port, project, runtime, timeout = 30000) {
|
|
|
54
54
|
});
|
|
55
55
|
while (true) {
|
|
56
56
|
try {
|
|
57
|
-
res = await Promise.race([(0, node_fetch_1.default)(`http://
|
|
57
|
+
res = await Promise.race([(0, node_fetch_1.default)(`http://127.0.0.1:${port}/__/functions.yaml`), timedOut]);
|
|
58
58
|
break;
|
|
59
59
|
}
|
|
60
60
|
catch (err) {
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getRuntimeDelegate = exports.getHumanFriendlyRuntimeName = exports.isValidRuntime = exports.isDeprecatedRuntime = void 0;
|
|
4
4
|
const node = require("./node");
|
|
5
|
+
const python = require("./python");
|
|
5
6
|
const validate = require("../validate");
|
|
6
7
|
const error_1 = require("../../../error");
|
|
7
8
|
const RUNTIMES = ["nodejs10", "nodejs12", "nodejs14", "nodejs16", "nodejs18"];
|
|
8
|
-
const EXPERIMENTAL_RUNTIMES = ["
|
|
9
|
+
const EXPERIMENTAL_RUNTIMES = ["python310", "python311"];
|
|
9
10
|
const DEPRECATED_RUNTIMES = ["nodejs6", "nodejs8"];
|
|
10
11
|
function isDeprecatedRuntime(runtime) {
|
|
11
12
|
return DEPRECATED_RUNTIMES.includes(runtime);
|
|
@@ -23,13 +24,14 @@ const MESSAGE_FRIENDLY_RUNTIMES = {
|
|
|
23
24
|
nodejs14: "Node.js 14",
|
|
24
25
|
nodejs16: "Node.js 16",
|
|
25
26
|
nodejs18: "Node.js 18",
|
|
26
|
-
|
|
27
|
+
python310: "Python 3.10",
|
|
28
|
+
python311: "Python 3.11 (Preview)",
|
|
27
29
|
};
|
|
28
30
|
function getHumanFriendlyRuntimeName(runtime) {
|
|
29
31
|
return MESSAGE_FRIENDLY_RUNTIMES[runtime] || runtime;
|
|
30
32
|
}
|
|
31
33
|
exports.getHumanFriendlyRuntimeName = getHumanFriendlyRuntimeName;
|
|
32
|
-
const factories = [node.tryCreateDelegate];
|
|
34
|
+
const factories = [node.tryCreateDelegate, python.tryCreateDelegate];
|
|
33
35
|
async function getRuntimeDelegate(context) {
|
|
34
36
|
const { projectDir, sourceDir, runtime } = context;
|
|
35
37
|
validate.functionsDirectoryExists(sourceDir, projectDir);
|
|
@@ -16,6 +16,7 @@ const discovery = require("../discovery");
|
|
|
16
16
|
const validate = require("./validate");
|
|
17
17
|
const versioning = require("./versioning");
|
|
18
18
|
const parseTriggers = require("./parseTriggers");
|
|
19
|
+
const fsutils_1 = require("../../../../fsutils");
|
|
19
20
|
const MIN_FUNCTIONS_SDK_VERSION = "3.20.0";
|
|
20
21
|
async function tryCreateDelegate(context) {
|
|
21
22
|
const packageJsonPath = path.join(context.sourceDir, "package.json");
|
|
@@ -39,6 +40,7 @@ class Delegate {
|
|
|
39
40
|
this.runtime = runtime;
|
|
40
41
|
this.name = "nodejs";
|
|
41
42
|
this._sdkVersion = undefined;
|
|
43
|
+
this._bin = "";
|
|
42
44
|
}
|
|
43
45
|
get sdkVersion() {
|
|
44
46
|
if (this._sdkVersion === undefined) {
|
|
@@ -46,6 +48,38 @@ class Delegate {
|
|
|
46
48
|
}
|
|
47
49
|
return this._sdkVersion;
|
|
48
50
|
}
|
|
51
|
+
get bin() {
|
|
52
|
+
if (this._bin === "") {
|
|
53
|
+
this._bin = this.getNodeBinary();
|
|
54
|
+
}
|
|
55
|
+
return this._bin;
|
|
56
|
+
}
|
|
57
|
+
getNodeBinary() {
|
|
58
|
+
const requestedVersion = semver.coerce(this.runtime);
|
|
59
|
+
if (!requestedVersion) {
|
|
60
|
+
throw new error_1.FirebaseError(`Could not determine version of the requested runtime: ${this.runtime}`);
|
|
61
|
+
}
|
|
62
|
+
const hostVersion = process.versions.node;
|
|
63
|
+
const localNodePath = path.join(this.sourceDir, "node_modules/node");
|
|
64
|
+
const localNodeVersion = versioning.findModuleVersion("node", localNodePath);
|
|
65
|
+
if (localNodeVersion) {
|
|
66
|
+
if (semver.major(requestedVersion) === semver.major(localNodeVersion)) {
|
|
67
|
+
(0, utils_1.logLabeledSuccess)("functions", `Using node@${semver.major(localNodeVersion)} from local cache.`);
|
|
68
|
+
return localNodePath;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (semver.major(requestedVersion) === semver.major(hostVersion)) {
|
|
72
|
+
(0, utils_1.logLabeledSuccess)("functions", `Using node@${semver.major(hostVersion)} from host.`);
|
|
73
|
+
return process.execPath;
|
|
74
|
+
}
|
|
75
|
+
if (!process.env.FIREPIT_VERSION) {
|
|
76
|
+
(0, utils_1.logLabeledWarning)("functions", `Your requested "node" version "${semver.major(requestedVersion)}" doesn't match your global version "${semver.major(hostVersion)}". Using node@${semver.major(hostVersion)} from host.`);
|
|
77
|
+
return process.execPath;
|
|
78
|
+
}
|
|
79
|
+
(0, utils_1.logLabeledWarning)("functions", `You've requested "node" version "${semver.major(requestedVersion)}", but the standalone Firebase CLI comes with bundled Node "${semver.major(hostVersion)}".`);
|
|
80
|
+
(0, utils_1.logLabeledSuccess)("functions", `To use a different Node.js version, consider removing the standalone Firebase CLI and switching to "firebase-tools" on npm.`);
|
|
81
|
+
return process.execPath;
|
|
82
|
+
}
|
|
49
83
|
validate() {
|
|
50
84
|
versioning.checkFunctionsSDKVersion(this.sdkVersion);
|
|
51
85
|
const relativeDir = path.relative(this.projectDir, this.sourceDir);
|
|
@@ -57,33 +91,44 @@ class Delegate {
|
|
|
57
91
|
watch() {
|
|
58
92
|
return Promise.resolve(() => Promise.resolve());
|
|
59
93
|
}
|
|
60
|
-
|
|
94
|
+
serveAdmin(port, config, envs) {
|
|
61
95
|
var _a;
|
|
62
|
-
const env = Object.assign(Object.assign({}, envs), { PORT: port
|
|
96
|
+
const env = Object.assign(Object.assign({}, envs), { PORT: port, FUNCTIONS_CONTROL_API: "true", HOME: process.env.HOME, PATH: process.env.PATH, NODE_ENV: process.env.NODE_ENV });
|
|
63
97
|
if (Object.keys(config || {}).length) {
|
|
64
98
|
env.CLOUD_RUNTIME_CONFIG = JSON.stringify(config);
|
|
65
99
|
}
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
100
|
+
const sourceNodeModulesPath = path.join(this.sourceDir, "node_modules");
|
|
101
|
+
const sdkPath = require.resolve("firebase-functions", { paths: [this.sourceDir] });
|
|
102
|
+
const sdkNodeModulesPath = sdkPath.substring(0, sdkPath.lastIndexOf("node_modules") + 12);
|
|
103
|
+
for (const nodeModulesPath of [sourceNodeModulesPath, sdkNodeModulesPath]) {
|
|
104
|
+
const binPath = path.join(nodeModulesPath, ".bin", "firebase-functions");
|
|
105
|
+
if ((0, fsutils_1.fileExistsSync)(binPath)) {
|
|
106
|
+
logger_1.logger.debug(`Found firebase-functions binary at '${binPath}'`);
|
|
107
|
+
const childProcess = spawn(binPath, [this.sourceDir], {
|
|
108
|
+
env,
|
|
109
|
+
cwd: this.sourceDir,
|
|
110
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
111
|
+
});
|
|
112
|
+
(_a = childProcess.stdout) === null || _a === void 0 ? void 0 : _a.on("data", (chunk) => {
|
|
113
|
+
logger_1.logger.debug(chunk.toString());
|
|
114
|
+
});
|
|
115
|
+
return Promise.resolve(async () => {
|
|
116
|
+
const p = new Promise((resolve, reject) => {
|
|
117
|
+
childProcess.once("exit", resolve);
|
|
118
|
+
childProcess.once("error", reject);
|
|
119
|
+
});
|
|
120
|
+
await (0, node_fetch_1.default)(`http://localhost:${port}/__/quitquitquit`);
|
|
121
|
+
setTimeout(() => {
|
|
122
|
+
if (!childProcess.killed) {
|
|
123
|
+
childProcess.kill("SIGKILL");
|
|
124
|
+
}
|
|
125
|
+
}, 10000);
|
|
126
|
+
return p;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
throw new error_1.FirebaseError("Failed to find location of Firebase Functions SDK. " +
|
|
131
|
+
"Please file a bug on Github (https://github.com/firebase/firebase-tools/).");
|
|
87
132
|
}
|
|
88
133
|
async discoverBuild(config, env) {
|
|
89
134
|
if (!semver.valid(this.sdkVersion)) {
|
|
@@ -97,9 +142,9 @@ class Delegate {
|
|
|
97
142
|
}
|
|
98
143
|
let discovered = await discovery.detectFromYaml(this.sourceDir, this.projectId, this.runtime);
|
|
99
144
|
if (!discovered) {
|
|
100
|
-
const
|
|
101
|
-
const port = await
|
|
102
|
-
const kill = await this.
|
|
145
|
+
const basePort = 8000 + (0, utils_1.randomInt)(0, 1000);
|
|
146
|
+
const port = await portfinder.getPortPromise({ port: basePort });
|
|
147
|
+
const kill = await this.serveAdmin(port.toString(), config, env);
|
|
103
148
|
try {
|
|
104
149
|
discovered = await discovery.detectFromPort(port, this.projectId, this.runtime);
|
|
105
150
|
}
|
|
@@ -4,12 +4,13 @@ exports.checkFunctionsSDKVersion = exports.getLatestSDKVersion = exports.getFunc
|
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const clc = require("colorette");
|
|
7
|
-
const semver = require("semver");
|
|
8
7
|
const spawn = require("cross-spawn");
|
|
9
|
-
const
|
|
8
|
+
const semver = require("semver");
|
|
10
9
|
const logger_1 = require("../../../../logger");
|
|
11
10
|
const track_1 = require("../../../../track");
|
|
11
|
+
const utils = require("../../../../utils");
|
|
12
12
|
const MIN_SDK_VERSION = "2.0.0";
|
|
13
|
+
const NPM_COMMAND_TIMEOUT_MILLIES = 10000;
|
|
13
14
|
exports.FUNCTIONS_SDK_VERSION_TOO_OLD_WARNING = clc.bold(clc.yellow("functions: ")) +
|
|
14
15
|
"You must have a " +
|
|
15
16
|
clc.bold("firebase-functions") +
|
|
@@ -53,6 +54,7 @@ function getLatestSDKVersion() {
|
|
|
53
54
|
var _a;
|
|
54
55
|
const child = spawn.sync("npm", ["show", "firebase-functions", "--json=true"], {
|
|
55
56
|
encoding: "utf8",
|
|
57
|
+
timeout: NPM_COMMAND_TIMEOUT_MILLIES,
|
|
56
58
|
});
|
|
57
59
|
if (child.error) {
|
|
58
60
|
logger_1.logger.debug("checkFunctionsSDKVersion was unable to fetch information from NPM", child.error.stack);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Delegate = exports.tryCreateDelegate = void 0;
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const node_fetch_1 = require("node-fetch");
|
|
7
|
+
const util_1 = require("util");
|
|
8
|
+
const portfinder = require("portfinder");
|
|
9
|
+
const runtimes = require("..");
|
|
10
|
+
const discovery = require("../discovery");
|
|
11
|
+
const logger_1 = require("../../../../logger");
|
|
12
|
+
const python_1 = require("../../../../functions/python");
|
|
13
|
+
const error_1 = require("../../../../error");
|
|
14
|
+
const LATEST_VERSION = "python310";
|
|
15
|
+
async function tryCreateDelegate(context) {
|
|
16
|
+
const requirementsTextPath = path.join(context.sourceDir, "requirements.txt");
|
|
17
|
+
if (!(await (0, util_1.promisify)(fs.exists)(requirementsTextPath))) {
|
|
18
|
+
logger_1.logger.debug("Customer code is not Python code.");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const runtime = context.runtime ? context.runtime : LATEST_VERSION;
|
|
22
|
+
if (!runtimes.isValidRuntime(runtime)) {
|
|
23
|
+
throw new error_1.FirebaseError(`Runtime ${runtime} is not a valid Python runtime`);
|
|
24
|
+
}
|
|
25
|
+
return Promise.resolve(new Delegate(context.projectId, context.sourceDir, runtime));
|
|
26
|
+
}
|
|
27
|
+
exports.tryCreateDelegate = tryCreateDelegate;
|
|
28
|
+
class Delegate {
|
|
29
|
+
constructor(projectId, sourceDir, runtime) {
|
|
30
|
+
this.projectId = projectId;
|
|
31
|
+
this.sourceDir = sourceDir;
|
|
32
|
+
this.runtime = runtime;
|
|
33
|
+
this.name = "python";
|
|
34
|
+
this._bin = "";
|
|
35
|
+
this._modulesDir = "";
|
|
36
|
+
}
|
|
37
|
+
get bin() {
|
|
38
|
+
if (this._bin === "") {
|
|
39
|
+
this._bin = this.getPythonBinary();
|
|
40
|
+
}
|
|
41
|
+
return this._bin;
|
|
42
|
+
}
|
|
43
|
+
async modulesDir() {
|
|
44
|
+
var _a;
|
|
45
|
+
if (!this._modulesDir) {
|
|
46
|
+
const child = (0, python_1.runWithVirtualEnv)([
|
|
47
|
+
this.bin,
|
|
48
|
+
"-c",
|
|
49
|
+
'"import firebase_functions; import os; print(os.path.dirname(firebase_functions.__file__))"',
|
|
50
|
+
], this.sourceDir, {});
|
|
51
|
+
let out = "";
|
|
52
|
+
(_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on("data", (chunk) => {
|
|
53
|
+
const chunkString = chunk.toString();
|
|
54
|
+
out = out + chunkString;
|
|
55
|
+
logger_1.logger.debug(`stdout: ${chunkString}`);
|
|
56
|
+
});
|
|
57
|
+
await new Promise((resolve, reject) => {
|
|
58
|
+
child.on("exit", resolve);
|
|
59
|
+
child.on("error", reject);
|
|
60
|
+
});
|
|
61
|
+
this._modulesDir = out.trim();
|
|
62
|
+
}
|
|
63
|
+
return this._modulesDir;
|
|
64
|
+
}
|
|
65
|
+
getPythonBinary() {
|
|
66
|
+
if (process.platform === "win32") {
|
|
67
|
+
return "python.exe";
|
|
68
|
+
}
|
|
69
|
+
if (this.runtime === "python310") {
|
|
70
|
+
return "python3.10";
|
|
71
|
+
}
|
|
72
|
+
else if (this.runtime === "python311") {
|
|
73
|
+
return "python3.11";
|
|
74
|
+
}
|
|
75
|
+
return "python";
|
|
76
|
+
}
|
|
77
|
+
validate() {
|
|
78
|
+
return Promise.resolve();
|
|
79
|
+
}
|
|
80
|
+
watch() {
|
|
81
|
+
return Promise.resolve(() => Promise.resolve());
|
|
82
|
+
}
|
|
83
|
+
async build() {
|
|
84
|
+
return Promise.resolve();
|
|
85
|
+
}
|
|
86
|
+
async serveAdmin(port, envs) {
|
|
87
|
+
const modulesDir = await this.modulesDir();
|
|
88
|
+
const envWithAdminPort = Object.assign(Object.assign({}, envs), { ADMIN_PORT: port.toString() });
|
|
89
|
+
const args = [this.bin, path.join(modulesDir, "private", "serving.py")];
|
|
90
|
+
logger_1.logger.debug(`Running admin server with args: ${JSON.stringify(args)} and env: ${JSON.stringify(envWithAdminPort)} in ${this.sourceDir}`);
|
|
91
|
+
const childProcess = (0, python_1.runWithVirtualEnv)(args, this.sourceDir, envWithAdminPort);
|
|
92
|
+
return Promise.resolve(async () => {
|
|
93
|
+
await (0, node_fetch_1.default)(`http://127.0.0.1:${port}/__/quitquitquit`);
|
|
94
|
+
const quitTimeout = setTimeout(() => {
|
|
95
|
+
if (!childProcess.killed) {
|
|
96
|
+
childProcess.kill("SIGKILL");
|
|
97
|
+
}
|
|
98
|
+
}, 10000);
|
|
99
|
+
clearTimeout(quitTimeout);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
async discoverBuild(_configValues, envs) {
|
|
103
|
+
let discovered = await discovery.detectFromYaml(this.sourceDir, this.projectId, this.runtime);
|
|
104
|
+
if (!discovered) {
|
|
105
|
+
const adminPort = await portfinder.getPortPromise({
|
|
106
|
+
port: 8081,
|
|
107
|
+
});
|
|
108
|
+
const killProcess = await this.serveAdmin(adminPort, envs);
|
|
109
|
+
try {
|
|
110
|
+
discovered = await discovery.detectFromPort(adminPort, this.projectId, this.runtime);
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
await killProcess();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return discovered;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
exports.Delegate = Delegate;
|
|
@@ -4562,6 +4562,10 @@ exports.default = {
|
|
|
4562
4562
|
description: "Response message for GetRecaptchaParam.",
|
|
4563
4563
|
properties: {
|
|
4564
4564
|
kind: { type: "string" },
|
|
4565
|
+
producerProjectNumber: {
|
|
4566
|
+
description: "The producer project number used to generate PIA tokens",
|
|
4567
|
+
type: "string",
|
|
4568
|
+
},
|
|
4565
4569
|
recaptchaSiteKey: {
|
|
4566
4570
|
description: "The reCAPTCHA v2 site key used to invoke the reCAPTCHA service. Always present.",
|
|
4567
4571
|
type: "string",
|
|
@@ -502,7 +502,7 @@ function createSessionCookie(state, reqBody) {
|
|
|
502
502
|
const { payload } = parseIdToken(state, reqBody.idToken);
|
|
503
503
|
const issuedAt = (0, utils_1.toUnixTimestamp)(new Date());
|
|
504
504
|
const expiresAt = issuedAt + validDuration;
|
|
505
|
-
const sessionCookie = (0, jsonwebtoken_1.sign)(Object.assign(Object.assign({}, payload), { iat: issuedAt, exp: expiresAt, iss: `https://session.firebase.google.com/${payload.aud}` }), "", {
|
|
505
|
+
const sessionCookie = (0, jsonwebtoken_1.sign)(Object.assign(Object.assign({}, payload), { iat: issuedAt, exp: expiresAt, iss: `https://session.firebase.google.com/${payload.aud}` }), "fake-secret", {
|
|
506
506
|
algorithm: "none",
|
|
507
507
|
});
|
|
508
508
|
return { sessionCookie };
|
|
@@ -1552,7 +1552,7 @@ function generateJwt(user, { projectId, signInProvider, expiresInSeconds, extraC
|
|
|
1552
1552
|
tenant: tenantId,
|
|
1553
1553
|
sign_in_attributes: signInAttributes,
|
|
1554
1554
|
} });
|
|
1555
|
-
const jwtStr = (0, jsonwebtoken_1.sign)(customPayloadFields, "", {
|
|
1555
|
+
const jwtStr = (0, jsonwebtoken_1.sign)(customPayloadFields, "fake-secret", {
|
|
1556
1556
|
algorithm: "none",
|
|
1557
1557
|
expiresIn: expiresInSeconds,
|
|
1558
1558
|
subject: user.localId,
|
|
@@ -2129,7 +2129,7 @@ function generateBlockingFunctionJwt(state, event, url, timeoutMs, user, options
|
|
|
2129
2129
|
if (state.shouldForwardCredentialToBlockingFunction("refreshToken")) {
|
|
2130
2130
|
jwt.oauth_refresh_token = oauthTokens.oauthRefreshToken;
|
|
2131
2131
|
}
|
|
2132
|
-
const jwtStr = (0, jsonwebtoken_1.sign)(jwt, "", {
|
|
2132
|
+
const jwtStr = (0, jsonwebtoken_1.sign)(jwt, "fake-secret", {
|
|
2133
2133
|
algorithm: "none",
|
|
2134
2134
|
});
|
|
2135
2135
|
return jwtStr;
|
|
@@ -11,7 +11,6 @@ const registry_1 = require("./registry");
|
|
|
11
11
|
const types_1 = require("./types");
|
|
12
12
|
const constants_1 = require("./constants");
|
|
13
13
|
const functionsEmulator_1 = require("./functionsEmulator");
|
|
14
|
-
const functionsEmulatorUtils_1 = require("./functionsEmulatorUtils");
|
|
15
14
|
const auth_1 = require("./auth");
|
|
16
15
|
const databaseEmulator_1 = require("./databaseEmulator");
|
|
17
16
|
const firestoreEmulator_1 = require("./firestoreEmulator");
|
|
@@ -152,7 +151,7 @@ function findExportMetadata(importPath) {
|
|
|
152
151
|
}
|
|
153
152
|
}
|
|
154
153
|
async function startAll(options, showUI = true) {
|
|
155
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
154
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
156
155
|
const targets = filterEmulatorTargets(options);
|
|
157
156
|
options.targets = targets;
|
|
158
157
|
const singleProjectModeEnabled = ((_a = options.config.src.emulators) === null || _a === void 0 ? void 0 : _a.singleProjectMode) === undefined ||
|
|
@@ -301,13 +300,14 @@ async function startAll(options, showUI = true) {
|
|
|
301
300
|
utils.assertIsStringOrUndefined(options.extDevDir);
|
|
302
301
|
for (const cfg of functionsCfg) {
|
|
303
302
|
const functionsDir = path.join(projectDir, cfg.source);
|
|
303
|
+
const runtime = (_e = options.extDevRuntime) !== null && _e !== void 0 ? _e : cfg.runtime;
|
|
304
304
|
emulatableBackends.push({
|
|
305
305
|
functionsDir,
|
|
306
|
+
runtime,
|
|
306
307
|
codebase: cfg.codebase,
|
|
307
308
|
env: Object.assign({}, options.extDevEnv),
|
|
308
309
|
secretEnv: [],
|
|
309
310
|
predefinedTriggers: options.extDevTriggers,
|
|
310
|
-
nodeMajorVersion: (0, functionsEmulatorUtils_1.parseRuntimeVersion)(options.extDevNodeVersion || cfg.runtime),
|
|
311
311
|
});
|
|
312
312
|
}
|
|
313
313
|
}
|
|
@@ -316,7 +316,7 @@ async function startAll(options, showUI = true) {
|
|
|
316
316
|
}
|
|
317
317
|
if (emulatableBackends.length) {
|
|
318
318
|
if (!listenForEmulator.functions || !listenForEmulator.eventarc) {
|
|
319
|
-
listenForEmulator = await (0, portUtils_1.resolveHostAndAssignPorts)(Object.assign(Object.assign({}, listenForEmulator), { functions: (
|
|
319
|
+
listenForEmulator = await (0, portUtils_1.resolveHostAndAssignPorts)(Object.assign(Object.assign({}, listenForEmulator), { functions: (_f = listenForEmulator.functions) !== null && _f !== void 0 ? _f : getListenConfig(options, types_1.Emulators.FUNCTIONS), eventarc: (_g = listenForEmulator.eventarc) !== null && _g !== void 0 ? _g : getListenConfig(options, types_1.Emulators.EVENTARC) }));
|
|
320
320
|
hubLogger.log("DEBUG", "late-assigned ports for functions and eventarc emulators", {
|
|
321
321
|
user: listenForEmulator,
|
|
322
322
|
});
|
|
@@ -377,7 +377,7 @@ async function startAll(options, showUI = true) {
|
|
|
377
377
|
});
|
|
378
378
|
}
|
|
379
379
|
const config = options.config;
|
|
380
|
-
const rulesLocalPath = (
|
|
380
|
+
const rulesLocalPath = (_h = config.src.firestore) === null || _h === void 0 ? void 0 : _h.rules;
|
|
381
381
|
let rulesFileFound = false;
|
|
382
382
|
if (rulesLocalPath) {
|
|
383
383
|
const rules = config.path(rulesLocalPath);
|
|
@@ -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.3",
|
|
32
|
+
expectedSize: 52892936,
|
|
33
|
+
expectedChecksum: "2ca11ec1193003bea89f806cc085fa25",
|
|
34
34
|
},
|
|
35
35
|
ui: experiments.isEnabled("emulatoruisnapshot")
|
|
36
36
|
? { version: "SNAPSHOT", expectedSize: -1, expectedChecksum: "" }
|
|
@@ -143,14 +143,15 @@ class ExtensionsEmulator {
|
|
|
143
143
|
const extensionDir = await this.ensureSourceCode(instance);
|
|
144
144
|
const functionsDir = path.join(extensionDir, "functions");
|
|
145
145
|
const env = Object.assign(this.autoPopulatedParams(instance), instance.params);
|
|
146
|
-
const { extensionTriggers,
|
|
146
|
+
const { extensionTriggers, runtime, nonSecretEnv, secretEnvVariables } = await (0, optionsHelper_1.getExtensionFunctionInfo)(instance, env);
|
|
147
147
|
const emulatableBackend = {
|
|
148
148
|
functionsDir,
|
|
149
|
+
runtime,
|
|
150
|
+
bin: process.execPath,
|
|
149
151
|
env: nonSecretEnv,
|
|
150
152
|
codebase: instance.instanceId,
|
|
151
153
|
secretEnv: secretEnvVariables,
|
|
152
154
|
predefinedTriggers: extensionTriggers,
|
|
153
|
-
nodeMajorVersion: nodeMajorVersion,
|
|
154
155
|
extensionInstanceId: instance.instanceId,
|
|
155
156
|
};
|
|
156
157
|
if (instance.ref) {
|