firebase-tools 13.6.0 → 13.7.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 +1 -1
- package/lib/apphosting/config.js +31 -0
- package/lib/apphosting/githubConnections.js +261 -0
- package/lib/{init/features/apphosting → apphosting}/index.js +21 -17
- package/lib/{init/features/apphosting → apphosting}/repo.js +9 -9
- package/lib/apphosting/secrets/dialogs.js +169 -0
- package/lib/apphosting/secrets/index.js +98 -0
- package/lib/commands/apphosting-backends-create.js +4 -2
- package/lib/commands/apphosting-backends-delete.js +1 -1
- package/lib/commands/apphosting-secrets-access.js +24 -0
- package/lib/commands/apphosting-secrets-describe.js +29 -0
- package/lib/commands/apphosting-secrets-grantaccess.js +45 -0
- package/lib/commands/apphosting-secrets-set.js +105 -0
- package/lib/commands/functions-secrets-access.js +2 -2
- package/lib/commands/functions-secrets-describe.js +14 -0
- package/lib/commands/functions-secrets-destroy.js +2 -2
- package/lib/commands/functions-secrets-get.js +3 -17
- package/lib/commands/functions-secrets-prune.js +2 -1
- package/lib/commands/functions-secrets-set.js +2 -2
- package/lib/commands/index.js +6 -0
- package/lib/deploy/functions/checkIam.js +3 -6
- package/lib/deploy/functions/containerCleaner.js +1 -11
- package/lib/deploy/functions/params.js +2 -2
- package/lib/deploy/functions/prepare.js +12 -3
- package/lib/deploy/functions/prompts.js +39 -7
- package/lib/deploy/functions/release/fabricator.js +5 -5
- package/lib/deploy/functions/release/index.js +17 -2
- package/lib/deploy/functions/release/planner.js +11 -3
- package/lib/deploy/functions/runtimes/index.js +6 -43
- package/lib/deploy/functions/runtimes/node/index.js +3 -2
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +15 -34
- package/lib/deploy/functions/runtimes/python/index.js +11 -7
- package/lib/deploy/functions/runtimes/supported.js +135 -0
- package/lib/deploy/functions/services/index.js +4 -0
- package/lib/emulator/controller.js +8 -1
- package/lib/emulator/functionsEmulator.js +2 -2
- package/lib/emulator/hub.js +5 -0
- package/lib/experiments.js +12 -0
- package/lib/extensions/emulator/specHelper.js +4 -3
- package/lib/frameworks/next/constants.js +2 -1
- package/lib/frameworks/next/index.js +22 -12
- package/lib/frameworks/next/utils.js +32 -3
- package/lib/functional.js +2 -2
- package/lib/functions/events/v2.js +7 -1
- package/lib/functions/secrets.js +40 -22
- package/lib/gcp/apphosting.js +15 -2
- package/lib/gcp/cloudbuild.js +7 -3
- package/lib/gcp/cloudfunctions.js +5 -5
- package/lib/gcp/cloudfunctionsv2.js +3 -3
- package/lib/gcp/cloudscheduler.js +2 -2
- package/lib/gcp/computeEngine.js +7 -0
- package/lib/gcp/devConnect.js +24 -11
- package/lib/gcp/iam.js +9 -1
- package/lib/gcp/secretManager.js +53 -13
- package/lib/gcp/serviceusage.js +21 -5
- package/lib/init/features/functions/python.js +4 -3
- package/lib/init/features/index.js +1 -1
- package/lib/utils.js +6 -6
- package/package.json +1 -1
- package/schema/firebase-config.json +12 -2
- /package/lib/{init/features/apphosting → apphosting}/constants.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.promptForMinInstances = exports.promptForFunctionDeletion = exports.promptForFailurePolicies = void 0;
|
|
3
|
+
exports.promptForMinInstances = exports.promptForUnsafeMigration = exports.promptForFunctionDeletion = exports.promptForFailurePolicies = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
5
|
const functionsDeployHelper_1 = require("./functionsDeployHelper");
|
|
6
6
|
const error_1 = require("../../error");
|
|
@@ -49,16 +49,16 @@ async function promptForFailurePolicies(options, want, have) {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
exports.promptForFailurePolicies = promptForFailurePolicies;
|
|
52
|
-
async function promptForFunctionDeletion(functionsToDelete,
|
|
52
|
+
async function promptForFunctionDeletion(functionsToDelete, options) {
|
|
53
53
|
let shouldDeleteFns = true;
|
|
54
|
-
if (functionsToDelete.length === 0 || force) {
|
|
54
|
+
if (functionsToDelete.length === 0 || options.force) {
|
|
55
55
|
return true;
|
|
56
56
|
}
|
|
57
57
|
const deleteList = functionsToDelete
|
|
58
58
|
.sort(backend.compareFunctions)
|
|
59
59
|
.map((fn) => "\t" + (0, functionsDeployHelper_1.getFunctionLabel)(fn))
|
|
60
60
|
.join("\n");
|
|
61
|
-
if (nonInteractive) {
|
|
61
|
+
if (options.nonInteractive) {
|
|
62
62
|
const deleteCommands = functionsToDelete
|
|
63
63
|
.map((func) => {
|
|
64
64
|
return "\tfirebase functions:delete " + func.id + " --region " + func.region;
|
|
@@ -75,9 +75,7 @@ async function promptForFunctionDeletion(functionsToDelete, force, nonInteractiv
|
|
|
75
75
|
"\n\nIf you are renaming a function or changing its region, it is recommended that you create the new " +
|
|
76
76
|
"function first before deleting the old one to prevent event loss. For more info, visit " +
|
|
77
77
|
clc.underline("https://firebase.google.com/docs/functions/manage-functions#modify" + "\n"));
|
|
78
|
-
shouldDeleteFns = await (0, prompt_1.
|
|
79
|
-
type: "confirm",
|
|
80
|
-
name: "confirm",
|
|
78
|
+
shouldDeleteFns = await (0, prompt_1.confirm)({
|
|
81
79
|
default: false,
|
|
82
80
|
message: "Would you like to proceed with deletion? Selecting no will continue the rest of the deployments.",
|
|
83
81
|
});
|
|
@@ -85,6 +83,40 @@ async function promptForFunctionDeletion(functionsToDelete, force, nonInteractiv
|
|
|
85
83
|
return shouldDeleteFns;
|
|
86
84
|
}
|
|
87
85
|
exports.promptForFunctionDeletion = promptForFunctionDeletion;
|
|
86
|
+
async function promptForUnsafeMigration(fnsToUpdate, options) {
|
|
87
|
+
const unsafeUpdates = fnsToUpdate.filter((eu) => eu.unsafe);
|
|
88
|
+
if (unsafeUpdates.length === 0 || options.force) {
|
|
89
|
+
return fnsToUpdate;
|
|
90
|
+
}
|
|
91
|
+
const warnMessage = "The following functions are unsafely changing event types: " +
|
|
92
|
+
clc.bold(unsafeUpdates
|
|
93
|
+
.map((eu) => eu.endpoint)
|
|
94
|
+
.sort(backend.compareFunctions)
|
|
95
|
+
.map(functionsDeployHelper_1.getFunctionLabel)
|
|
96
|
+
.join(", ")) +
|
|
97
|
+
". " +
|
|
98
|
+
"While automatic migration is allowed for these functions, updating the underlying event type may result in data loss. " +
|
|
99
|
+
"To avoid this, consider the best practices outlined in the migration guide: https://firebase.google.com/docs/functions/manage-functions?gen=2nd#modify-trigger";
|
|
100
|
+
utils.logLabeledWarning("functions", warnMessage);
|
|
101
|
+
const safeUpdates = fnsToUpdate.filter((eu) => !eu.unsafe);
|
|
102
|
+
if (options.nonInteractive) {
|
|
103
|
+
utils.logLabeledWarning("functions", "Skipping updates for functions that may be unsafe to update. To update these functions anyway, deploy again in interactive mode or use the --force option.");
|
|
104
|
+
return safeUpdates;
|
|
105
|
+
}
|
|
106
|
+
for (const eu of unsafeUpdates) {
|
|
107
|
+
const shouldUpdate = await (0, prompt_1.promptOnce)({
|
|
108
|
+
type: "confirm",
|
|
109
|
+
name: "confirm",
|
|
110
|
+
default: false,
|
|
111
|
+
message: `[${(0, functionsDeployHelper_1.getFunctionLabel)(eu.endpoint)}] Would you like to proceed with the unsafe migration?`,
|
|
112
|
+
});
|
|
113
|
+
if (shouldUpdate) {
|
|
114
|
+
safeUpdates.push(eu);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return safeUpdates;
|
|
118
|
+
}
|
|
119
|
+
exports.promptForUnsafeMigration = promptForUnsafeMigration;
|
|
88
120
|
async function promptForMinInstances(options, want, have) {
|
|
89
121
|
if (options.force) {
|
|
90
122
|
return;
|
|
@@ -7,7 +7,7 @@ const error_1 = require("../../../error");
|
|
|
7
7
|
const sourceTokenScraper_1 = require("./sourceTokenScraper");
|
|
8
8
|
const timer_1 = require("./timer");
|
|
9
9
|
const functional_1 = require("../../../functional");
|
|
10
|
-
const
|
|
10
|
+
const supported_1 = require("../runtimes/supported");
|
|
11
11
|
const api_1 = require("../../../api");
|
|
12
12
|
const logger_1 = require("../../../logger");
|
|
13
13
|
const backend = require("../backend");
|
|
@@ -25,7 +25,7 @@ const scheduler = require("../../../gcp/cloudscheduler");
|
|
|
25
25
|
const utils = require("../../../utils");
|
|
26
26
|
const services = require("../services");
|
|
27
27
|
const v1_1 = require("../../../functions/events/v1");
|
|
28
|
-
const
|
|
28
|
+
const gce = require("../../../gcp/computeEngine");
|
|
29
29
|
const functionsDeployHelper_1 = require("../functionsDeployHelper");
|
|
30
30
|
const gcfV1PollerOptions = {
|
|
31
31
|
apiOrigin: (0, api_1.functionsOrigin)(),
|
|
@@ -332,7 +332,7 @@ class Fabricator {
|
|
|
332
332
|
else if (backend.isScheduleTriggered(endpoint)) {
|
|
333
333
|
const invoker = endpoint.serviceAccount
|
|
334
334
|
? [endpoint.serviceAccount]
|
|
335
|
-
: [
|
|
335
|
+
: [gce.getDefaultServiceAccount(this.projectNumber)];
|
|
336
336
|
await this.executor
|
|
337
337
|
.run(() => run.setInvokerCreate(endpoint.project, serviceName, invoker))
|
|
338
338
|
.catch(rethrowAs(endpoint, "set invoker"));
|
|
@@ -415,7 +415,7 @@ class Fabricator {
|
|
|
415
415
|
else if (backend.isScheduleTriggered(endpoint)) {
|
|
416
416
|
invoker = endpoint.serviceAccount
|
|
417
417
|
? [endpoint.serviceAccount]
|
|
418
|
-
: [
|
|
418
|
+
: [gce.getDefaultServiceAccount(this.projectNumber)];
|
|
419
419
|
}
|
|
420
420
|
if (invoker) {
|
|
421
421
|
await this.executor
|
|
@@ -562,7 +562,7 @@ class Fabricator {
|
|
|
562
562
|
.catch(rethrowAs(endpoint, "unregister blocking trigger"));
|
|
563
563
|
}
|
|
564
564
|
logOpStart(op, endpoint) {
|
|
565
|
-
const runtime =
|
|
565
|
+
const runtime = supported_1.RUNTIMES[endpoint.runtime].friendly;
|
|
566
566
|
const platform = (0, functionsDeployHelper_1.getHumanFriendlyPlatformName)(endpoint.platform);
|
|
567
567
|
const label = helper.getFunctionLabel(endpoint);
|
|
568
568
|
utils.logLabeledBullet("functions", `${op} ${runtime} (${platform}) function ${clc.bold(label)}...`);
|
|
@@ -11,6 +11,7 @@ const fabricator = require("./fabricator");
|
|
|
11
11
|
const reporter = require("./reporter");
|
|
12
12
|
const executor = require("./executor");
|
|
13
13
|
const prompts = require("../prompts");
|
|
14
|
+
const experiments = require("../../../experiments");
|
|
14
15
|
const functionsConfig_1 = require("../../../functionsConfig");
|
|
15
16
|
const functionsDeployHelper_1 = require("../functionsDeployHelper");
|
|
16
17
|
const error_1 = require("../../../error");
|
|
@@ -37,12 +38,24 @@ async function release(context, options, payload) {
|
|
|
37
38
|
const fnsToDelete = Object.values(plan)
|
|
38
39
|
.map((regionalChanges) => regionalChanges.endpointsToDelete)
|
|
39
40
|
.reduce(functional_1.reduceFlat, []);
|
|
40
|
-
const shouldDelete = await prompts.promptForFunctionDeletion(fnsToDelete, options
|
|
41
|
+
const shouldDelete = await prompts.promptForFunctionDeletion(fnsToDelete, options);
|
|
41
42
|
if (!shouldDelete) {
|
|
42
43
|
for (const change of Object.values(plan)) {
|
|
43
44
|
change.endpointsToDelete = [];
|
|
44
45
|
}
|
|
45
46
|
}
|
|
47
|
+
const fnsToUpdate = Object.values(plan)
|
|
48
|
+
.map((regionalChanges) => regionalChanges.endpointsToUpdate)
|
|
49
|
+
.reduce(functional_1.reduceFlat, []);
|
|
50
|
+
const fnsToUpdateSafe = await prompts.promptForUnsafeMigration(fnsToUpdate, options);
|
|
51
|
+
for (const key of Object.keys(plan)) {
|
|
52
|
+
plan[key].endpointsToUpdate = [];
|
|
53
|
+
}
|
|
54
|
+
for (const eu of fnsToUpdateSafe) {
|
|
55
|
+
const e = eu.endpoint;
|
|
56
|
+
const key = `${e.codebase || ""}-${e.region}-${e.availableMemoryMb || "default"}`;
|
|
57
|
+
plan[key].endpointsToUpdate.push(eu);
|
|
58
|
+
}
|
|
46
59
|
const throttlerOptions = {
|
|
47
60
|
retries: 30,
|
|
48
61
|
backoff: 20000,
|
|
@@ -65,7 +78,9 @@ async function release(context, options, payload) {
|
|
|
65
78
|
const deletedEndpoints = Object.values(plan)
|
|
66
79
|
.map((r) => r.endpointsToDelete)
|
|
67
80
|
.reduce(functional_1.reduceFlat, []);
|
|
68
|
-
|
|
81
|
+
if (experiments.isEnabled("automaticallydeletegcfartifacts")) {
|
|
82
|
+
await containerCleaner.cleanupBuildImages(haveEndpoints, deletedEndpoints);
|
|
83
|
+
}
|
|
69
84
|
const allErrors = summary.results.filter((r) => r.error).map((r) => r.error);
|
|
70
85
|
if (allErrors.length) {
|
|
71
86
|
const opts = allErrors.length === 1 ? { original: allErrors[0] } : { children: allErrors };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.checkForV2Upgrade = exports.checkForIllegalUpdate = exports.upgradedScheduleFromV1ToV2 = exports.changedV2PubSubTopic = exports.changedTriggerRegion = exports.upgradedToGCFv2WithoutSettingConcurrency = exports.createDeploymentPlan = exports.calculateUpdate = exports.calculateChangesets = void 0;
|
|
4
|
-
const clc = require("colorette");
|
|
3
|
+
exports.checkForV2Upgrade = exports.checkForIllegalUpdate = exports.checkForUnsafeUpdate = exports.upgradedScheduleFromV1ToV2 = exports.changedV2PubSubTopic = exports.changedTriggerRegion = exports.upgradedToGCFv2WithoutSettingConcurrency = exports.createDeploymentPlan = exports.calculateUpdate = exports.calculateChangesets = void 0;
|
|
5
4
|
const functionsDeployHelper_1 = require("../functionsDeployHelper");
|
|
6
5
|
const deploymentTool_1 = require("../../../deploymentTool");
|
|
7
6
|
const error_1 = require("../../../error");
|
|
8
7
|
const utils = require("../../../utils");
|
|
9
8
|
const backend = require("../backend");
|
|
10
9
|
const v2events = require("../../../functions/events/v2");
|
|
10
|
+
const v2_1 = require("../../../functions/events/v2");
|
|
11
11
|
function calculateChangesets(want, have, keyFn, deleteAll) {
|
|
12
12
|
const toCreate = utils.groupBy(Object.keys(want)
|
|
13
13
|
.filter((id) => !have[id])
|
|
@@ -29,7 +29,7 @@ function calculateChangesets(want, have, keyFn, deleteAll) {
|
|
|
29
29
|
}, {});
|
|
30
30
|
const toSkip = utils.groupBy(Object.values(toSkipEndpointsMap), keyFn);
|
|
31
31
|
if (Object.keys(toSkip).length) {
|
|
32
|
-
utils.logLabeledBullet("functions",
|
|
32
|
+
utils.logLabeledBullet("functions", "Skipping the deploy of unchanged functions.");
|
|
33
33
|
}
|
|
34
34
|
const toUpdate = utils.groupBy(Object.keys(want)
|
|
35
35
|
.filter((id) => have[id])
|
|
@@ -57,6 +57,7 @@ function calculateUpdate(want, have) {
|
|
|
57
57
|
checkForIllegalUpdate(want, have);
|
|
58
58
|
const update = {
|
|
59
59
|
endpoint: want,
|
|
60
|
+
unsafe: checkForUnsafeUpdate(want, have),
|
|
60
61
|
};
|
|
61
62
|
const needsDelete = changedTriggerRegion(want, have) ||
|
|
62
63
|
changedV2PubSubTopic(want, have) ||
|
|
@@ -164,6 +165,13 @@ function upgradedScheduleFromV1ToV2(want, have) {
|
|
|
164
165
|
return true;
|
|
165
166
|
}
|
|
166
167
|
exports.upgradedScheduleFromV1ToV2 = upgradedScheduleFromV1ToV2;
|
|
168
|
+
function checkForUnsafeUpdate(want, have) {
|
|
169
|
+
return (backend.isEventTriggered(want) &&
|
|
170
|
+
v2_1.FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX.test(want.eventTrigger.eventType) &&
|
|
171
|
+
backend.isEventTriggered(have) &&
|
|
172
|
+
v2_1.FIRESTORE_EVENT_REGEX.test(have.eventTrigger.eventType));
|
|
173
|
+
}
|
|
174
|
+
exports.checkForUnsafeUpdate = checkForUnsafeUpdate;
|
|
167
175
|
function checkForIllegalUpdate(want, have) {
|
|
168
176
|
const triggerType = (e) => {
|
|
169
177
|
if (backend.isHttpsTriggered(e)) {
|
|
@@ -1,61 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getRuntimeDelegate =
|
|
3
|
+
exports.getRuntimeDelegate = void 0;
|
|
4
4
|
const node = require("./node");
|
|
5
5
|
const python = require("./python");
|
|
6
6
|
const validate = require("../validate");
|
|
7
7
|
const error_1 = require("../../../error");
|
|
8
|
-
const
|
|
9
|
-
"nodejs10",
|
|
10
|
-
"nodejs12",
|
|
11
|
-
"nodejs14",
|
|
12
|
-
"nodejs16",
|
|
13
|
-
"nodejs18",
|
|
14
|
-
"nodejs20",
|
|
15
|
-
"python310",
|
|
16
|
-
"python311",
|
|
17
|
-
"python312",
|
|
18
|
-
];
|
|
19
|
-
const EXPERIMENTAL_RUNTIMES = [];
|
|
20
|
-
const DEPRECATED_RUNTIMES = ["nodejs6", "nodejs8"];
|
|
21
|
-
function isDeprecatedRuntime(runtime) {
|
|
22
|
-
return DEPRECATED_RUNTIMES.includes(runtime);
|
|
23
|
-
}
|
|
24
|
-
exports.isDeprecatedRuntime = isDeprecatedRuntime;
|
|
25
|
-
function isValidRuntime(runtime) {
|
|
26
|
-
return RUNTIMES.includes(runtime) || EXPERIMENTAL_RUNTIMES.includes(runtime);
|
|
27
|
-
}
|
|
28
|
-
exports.isValidRuntime = isValidRuntime;
|
|
29
|
-
const MESSAGE_FRIENDLY_RUNTIMES = {
|
|
30
|
-
nodejs6: "Node.js 6 (Deprecated)",
|
|
31
|
-
nodejs8: "Node.js 8 (Deprecated)",
|
|
32
|
-
nodejs10: "Node.js 10",
|
|
33
|
-
nodejs12: "Node.js 12",
|
|
34
|
-
nodejs14: "Node.js 14",
|
|
35
|
-
nodejs16: "Node.js 16",
|
|
36
|
-
nodejs18: "Node.js 18",
|
|
37
|
-
nodejs20: "Node.js 20",
|
|
38
|
-
python310: "Python 3.10",
|
|
39
|
-
python311: "Python 3.11",
|
|
40
|
-
python312: "Python 3.12",
|
|
41
|
-
};
|
|
42
|
-
function getHumanFriendlyRuntimeName(runtime) {
|
|
43
|
-
return MESSAGE_FRIENDLY_RUNTIMES[runtime] || runtime;
|
|
44
|
-
}
|
|
45
|
-
exports.getHumanFriendlyRuntimeName = getHumanFriendlyRuntimeName;
|
|
8
|
+
const supported = require("./supported");
|
|
46
9
|
const factories = [node.tryCreateDelegate, python.tryCreateDelegate];
|
|
47
10
|
async function getRuntimeDelegate(context) {
|
|
48
11
|
const { projectDir, sourceDir, runtime } = context;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
throw new error_1.FirebaseError(`Cannot deploy function with runtime ${runtime}`);
|
|
12
|
+
if (runtime && !supported.isRuntime(runtime)) {
|
|
13
|
+
throw new error_1.FirebaseError(`firebase.json specifies invalid runtime ${runtime} for directory ${sourceDir}`);
|
|
52
14
|
}
|
|
15
|
+
validate.functionsDirectoryExists(sourceDir, projectDir);
|
|
53
16
|
for (const factory of factories) {
|
|
54
17
|
const delegate = await factory(context);
|
|
55
18
|
if (delegate) {
|
|
56
19
|
return delegate;
|
|
57
20
|
}
|
|
58
21
|
}
|
|
59
|
-
throw new error_1.FirebaseError(`Could not detect
|
|
22
|
+
throw new error_1.FirebaseError(`Could not detect runtime for functions at ${sourceDir}`);
|
|
60
23
|
}
|
|
61
24
|
exports.getRuntimeDelegate = getRuntimeDelegate;
|
|
@@ -13,6 +13,7 @@ const parseRuntimeAndValidateSDK_1 = require("./parseRuntimeAndValidateSDK");
|
|
|
13
13
|
const logger_1 = require("../../../../logger");
|
|
14
14
|
const utils_1 = require("../../../../utils");
|
|
15
15
|
const discovery = require("../discovery");
|
|
16
|
+
const supported = require("../supported");
|
|
16
17
|
const validate = require("./validate");
|
|
17
18
|
const versioning = require("./versioning");
|
|
18
19
|
const parseTriggers = require("./parseTriggers");
|
|
@@ -25,7 +26,7 @@ async function tryCreateDelegate(context) {
|
|
|
25
26
|
return undefined;
|
|
26
27
|
}
|
|
27
28
|
const runtime = (0, parseRuntimeAndValidateSDK_1.getRuntimeChoice)(context.sourceDir, context.runtime);
|
|
28
|
-
if (!
|
|
29
|
+
if (!supported.runtimeIsLanguage(runtime, "nodejs")) {
|
|
29
30
|
logger_1.logger.debug("Customer has a package.json but did not get a nodejs runtime. This should not happen");
|
|
30
31
|
throw new error_1.FirebaseError(`Unexpected runtime ${runtime}`);
|
|
31
32
|
}
|
|
@@ -38,7 +39,7 @@ class Delegate {
|
|
|
38
39
|
this.projectDir = projectDir;
|
|
39
40
|
this.sourceDir = sourceDir;
|
|
40
41
|
this.runtime = runtime;
|
|
41
|
-
this.
|
|
42
|
+
this.language = "nodejs";
|
|
42
43
|
this._sdkVersion = undefined;
|
|
43
44
|
this._bin = "";
|
|
44
45
|
}
|
|
@@ -1,31 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getRuntimeChoice = exports.
|
|
3
|
+
exports.getRuntimeChoice = exports.RUNTIME_NOT_SET = void 0;
|
|
4
4
|
const path = require("path");
|
|
5
|
-
const clc = require("colorette");
|
|
6
5
|
const error_1 = require("../../../../error");
|
|
7
|
-
const
|
|
6
|
+
const supported = require("../supported");
|
|
8
7
|
const cjson = require("cjson");
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
14: "nodejs14",
|
|
15
|
-
16: "nodejs16",
|
|
16
|
-
18: "nodejs18",
|
|
17
|
-
20: "nodejs20",
|
|
18
|
-
};
|
|
19
|
-
const ENGINE_RUNTIMES_NAMES = Object.values(ENGINE_RUNTIMES);
|
|
20
|
-
exports.RUNTIME_NOT_SET = "`runtime` field is required but was not found in firebase.json.\n" +
|
|
8
|
+
const supportedNodeVersions = Object.keys(supported.RUNTIMES)
|
|
9
|
+
.filter((s) => supported.runtimeIsLanguage(s, "nodejs"))
|
|
10
|
+
.filter((s) => !supported.isDecommissioned(s))
|
|
11
|
+
.map((s) => s.substring("nodejs".length));
|
|
12
|
+
exports.RUNTIME_NOT_SET = "`runtime` field is required but was not found in firebase.json or package.json.\n" +
|
|
21
13
|
"To fix this, add the following lines to the `functions` section of your firebase.json:\n" +
|
|
22
|
-
|
|
23
|
-
exports.UNSUPPORTED_NODE_VERSION_FIREBASE_JSON_MSG = clc.bold(`functions.runtime value is unsupported. ` +
|
|
24
|
-
`Valid choices are: ${clc.bold("nodejs{10|12|14|16|18|20}")}.`);
|
|
25
|
-
exports.UNSUPPORTED_NODE_VERSION_PACKAGE_JSON_MSG = clc.bold(`package.json in functions directory has an engines field which is unsupported. ` +
|
|
26
|
-
`Valid choices are: ${clc.bold('{"node": 10|12|14|16|18|20}')}`);
|
|
27
|
-
exports.DEPRECATED_NODE_VERSION_INFO = `\n\nDeploys to runtimes below Node.js 10 are now disabled in the Firebase CLI. ` +
|
|
28
|
-
`${clc.bold(`Existing Node.js 8 functions ${clc.underline("will stop executing at a future date")}`)}. Update existing functions to Node.js 10 or greater as soon as possible.`;
|
|
14
|
+
`"runtime": "${supported.latest("nodejs")}" or set the "engine" field in package.json\n`;
|
|
29
15
|
function getRuntimeChoiceFromPackageJson(sourceDir) {
|
|
30
16
|
const packageJsonPath = path.join(sourceDir, "package.json");
|
|
31
17
|
let loaded;
|
|
@@ -39,19 +25,14 @@ function getRuntimeChoiceFromPackageJson(sourceDir) {
|
|
|
39
25
|
if (!engines || !engines.node) {
|
|
40
26
|
throw new error_1.FirebaseError(exports.RUNTIME_NOT_SET);
|
|
41
27
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const errorMessage = (runtimeFromConfig
|
|
47
|
-
? exports.UNSUPPORTED_NODE_VERSION_FIREBASE_JSON_MSG
|
|
48
|
-
: exports.UNSUPPORTED_NODE_VERSION_PACKAGE_JSON_MSG) + exports.DEPRECATED_NODE_VERSION_INFO;
|
|
49
|
-
if (!runtime || !ENGINE_RUNTIMES_NAMES.includes(runtime)) {
|
|
50
|
-
throw new error_1.FirebaseError(errorMessage, { exit: 1 });
|
|
51
|
-
}
|
|
52
|
-
if (runtimes.isDeprecatedRuntime(runtime) || !runtimes.isValidRuntime(runtime)) {
|
|
53
|
-
throw new error_1.FirebaseError(errorMessage, { exit: 1 });
|
|
28
|
+
const runtime = `nodejs${engines.node}`;
|
|
29
|
+
if (!supported.isRuntime(runtime)) {
|
|
30
|
+
throw new error_1.FirebaseError(`Detected node engine ${engines.node} in package.json, which is not a ` +
|
|
31
|
+
`supported version. Valid versions are ${supportedNodeVersions.join(", ")}`);
|
|
54
32
|
}
|
|
55
33
|
return runtime;
|
|
56
34
|
}
|
|
35
|
+
function getRuntimeChoice(sourceDir, runtimeFromConfig) {
|
|
36
|
+
return runtimeFromConfig || getRuntimeChoiceFromPackageJson(sourceDir);
|
|
37
|
+
}
|
|
57
38
|
exports.getRuntimeChoice = getRuntimeChoice;
|
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Delegate = exports.getPythonBinary = exports.tryCreateDelegate =
|
|
3
|
+
exports.Delegate = exports.getPythonBinary = exports.tryCreateDelegate = void 0;
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const node_fetch_1 = require("node-fetch");
|
|
7
7
|
const util_1 = require("util");
|
|
8
8
|
const portfinder = require("portfinder");
|
|
9
|
-
const runtimes = require("..");
|
|
10
9
|
const discovery = require("../discovery");
|
|
10
|
+
const supported = require("../supported");
|
|
11
11
|
const logger_1 = require("../../../../logger");
|
|
12
12
|
const python_1 = require("../../../../functions/python");
|
|
13
13
|
const error_1 = require("../../../../error");
|
|
14
|
-
|
|
14
|
+
const functional_1 = require("../../../../functional");
|
|
15
15
|
async function tryCreateDelegate(context) {
|
|
16
|
+
var _a;
|
|
16
17
|
const requirementsTextPath = path.join(context.sourceDir, "requirements.txt");
|
|
17
18
|
if (!(await (0, util_1.promisify)(fs.exists)(requirementsTextPath))) {
|
|
18
19
|
logger_1.logger.debug("Customer code is not Python code.");
|
|
19
20
|
return;
|
|
20
21
|
}
|
|
21
|
-
const runtime = context.runtime ?
|
|
22
|
-
if (!
|
|
22
|
+
const runtime = (_a = context.runtime) !== null && _a !== void 0 ? _a : supported.latest("python");
|
|
23
|
+
if (!supported.isRuntime(runtime)) {
|
|
23
24
|
throw new error_1.FirebaseError(`Runtime ${runtime} is not a valid Python runtime`);
|
|
24
25
|
}
|
|
26
|
+
if (!supported.runtimeIsLanguage(runtime, "python")) {
|
|
27
|
+
throw new error_1.FirebaseError(`Internal error. Trying to construct a python runtime delegate for runtime ${runtime}`, { exit: 1 });
|
|
28
|
+
}
|
|
25
29
|
return Promise.resolve(new Delegate(context.projectId, context.sourceDir, runtime));
|
|
26
30
|
}
|
|
27
31
|
exports.tryCreateDelegate = tryCreateDelegate;
|
|
@@ -38,7 +42,7 @@ function getPythonBinary(runtime) {
|
|
|
38
42
|
else if (runtime === "python312") {
|
|
39
43
|
return "python3.12";
|
|
40
44
|
}
|
|
41
|
-
|
|
45
|
+
(0, functional_1.assertExhaustive)(runtime, `Unhandled python runtime ${runtime}`);
|
|
42
46
|
}
|
|
43
47
|
exports.getPythonBinary = getPythonBinary;
|
|
44
48
|
class Delegate {
|
|
@@ -46,7 +50,7 @@ class Delegate {
|
|
|
46
50
|
this.projectId = projectId;
|
|
47
51
|
this.sourceDir = sourceDir;
|
|
48
52
|
this.runtime = runtime;
|
|
49
|
-
this.
|
|
53
|
+
this.language = "python";
|
|
50
54
|
this._bin = "";
|
|
51
55
|
this._modulesDir = "";
|
|
52
56
|
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.guardVersionSupport = exports.isDecommissioned = exports.latest = exports.runtimeIsLanguage = exports.isRuntime = exports.RUNTIMES = void 0;
|
|
4
|
+
const error_1 = require("../../../error");
|
|
5
|
+
const utils = require("../../../utils");
|
|
6
|
+
function runtimes(r) {
|
|
7
|
+
return r;
|
|
8
|
+
}
|
|
9
|
+
exports.RUNTIMES = runtimes({
|
|
10
|
+
nodejs6: {
|
|
11
|
+
friendly: "Node.js 6",
|
|
12
|
+
status: "decommissioned",
|
|
13
|
+
deprecationDate: "2019-04-17",
|
|
14
|
+
decommissionDate: "2020-08-01",
|
|
15
|
+
},
|
|
16
|
+
nodejs8: {
|
|
17
|
+
friendly: "Node.js 8",
|
|
18
|
+
status: "decommissioned",
|
|
19
|
+
deprecationDate: "2020-06-05",
|
|
20
|
+
decommissionDate: "2021-02-01",
|
|
21
|
+
},
|
|
22
|
+
nodejs10: {
|
|
23
|
+
friendly: "Node.js 10",
|
|
24
|
+
status: "GA",
|
|
25
|
+
deprecationDate: "2024-01-30",
|
|
26
|
+
decommissionDate: "2025-01-30",
|
|
27
|
+
},
|
|
28
|
+
nodejs12: {
|
|
29
|
+
friendly: "Node.js 12",
|
|
30
|
+
status: "GA",
|
|
31
|
+
deprecationDate: "2024-01-30",
|
|
32
|
+
decommissionDate: "2025-01-30",
|
|
33
|
+
},
|
|
34
|
+
nodejs14: {
|
|
35
|
+
friendly: "Node.js 14",
|
|
36
|
+
status: "GA",
|
|
37
|
+
deprecationDate: "2024-01-30",
|
|
38
|
+
decommissionDate: "2025-01-30",
|
|
39
|
+
},
|
|
40
|
+
nodejs16: {
|
|
41
|
+
friendly: "Node.js 16",
|
|
42
|
+
status: "GA",
|
|
43
|
+
deprecationDate: "2024-01-30",
|
|
44
|
+
decommissionDate: "2025-01-30",
|
|
45
|
+
},
|
|
46
|
+
nodejs18: {
|
|
47
|
+
friendly: "Node.js 18",
|
|
48
|
+
status: "GA",
|
|
49
|
+
deprecationDate: "2025-04-30",
|
|
50
|
+
decommissionDate: "2025-10-31",
|
|
51
|
+
},
|
|
52
|
+
nodejs20: {
|
|
53
|
+
friendly: "Node.js 20",
|
|
54
|
+
status: "GA",
|
|
55
|
+
deprecationDate: "2026-04-30",
|
|
56
|
+
decommissionDate: "2026-10-31",
|
|
57
|
+
},
|
|
58
|
+
python310: {
|
|
59
|
+
friendly: "Python 3.10",
|
|
60
|
+
status: "GA",
|
|
61
|
+
deprecationDate: "2026-10-04",
|
|
62
|
+
decommissionDate: "2027-04-30",
|
|
63
|
+
},
|
|
64
|
+
python311: {
|
|
65
|
+
friendly: "Python 3.11",
|
|
66
|
+
status: "GA",
|
|
67
|
+
deprecationDate: "2027-10-24",
|
|
68
|
+
decommissionDate: "2028-04-30",
|
|
69
|
+
},
|
|
70
|
+
python312: {
|
|
71
|
+
friendly: "Python 3.12",
|
|
72
|
+
status: "GA",
|
|
73
|
+
deprecationDate: "2028-10-02",
|
|
74
|
+
decommissionDate: "2029-04-30",
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
function isRuntime(maybe) {
|
|
78
|
+
return maybe in exports.RUNTIMES;
|
|
79
|
+
}
|
|
80
|
+
exports.isRuntime = isRuntime;
|
|
81
|
+
function runtimeIsLanguage(runtime, language) {
|
|
82
|
+
return runtime.startsWith(language);
|
|
83
|
+
}
|
|
84
|
+
exports.runtimeIsLanguage = runtimeIsLanguage;
|
|
85
|
+
function latest(language, runtimes = Object.keys(exports.RUNTIMES)) {
|
|
86
|
+
const sorted = runtimes
|
|
87
|
+
.filter((s) => runtimeIsLanguage(s, language))
|
|
88
|
+
.sort((left, right) => {
|
|
89
|
+
const leftVersion = +left.substring(language.length);
|
|
90
|
+
const rightVersion = +right.substring(language.length);
|
|
91
|
+
if (isNaN(leftVersion) || isNaN(rightVersion)) {
|
|
92
|
+
throw new error_1.FirebaseError("Internal error. Runtime or language names are malformed", {
|
|
93
|
+
exit: 1,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return leftVersion - rightVersion;
|
|
97
|
+
});
|
|
98
|
+
const latest = utils.last(sorted);
|
|
99
|
+
if (!latest) {
|
|
100
|
+
throw new error_1.FirebaseError(`Internal error trying to find the latest supported runtime for ${language}`, { exit: 1 });
|
|
101
|
+
}
|
|
102
|
+
return latest;
|
|
103
|
+
}
|
|
104
|
+
exports.latest = latest;
|
|
105
|
+
function isDecommissioned(runtime, now = new Date()) {
|
|
106
|
+
const cutoff = new Date(exports.RUNTIMES[runtime].decommissionDate);
|
|
107
|
+
return cutoff < now;
|
|
108
|
+
}
|
|
109
|
+
exports.isDecommissioned = isDecommissioned;
|
|
110
|
+
function guardVersionSupport(runtime, now = new Date()) {
|
|
111
|
+
const { deprecationDate, decommissionDate } = exports.RUNTIMES[runtime];
|
|
112
|
+
const decommission = new Date(decommissionDate);
|
|
113
|
+
if (now >= decommission) {
|
|
114
|
+
throw new error_1.FirebaseError(`Runtime ${exports.RUNTIMES[runtime].friendly} was decommissioned on ${decommissionDate}. To deploy ` +
|
|
115
|
+
"you must first upgrade your runtime version.", { exit: 1 });
|
|
116
|
+
}
|
|
117
|
+
const deprecation = new Date(deprecationDate);
|
|
118
|
+
if (now >= deprecation) {
|
|
119
|
+
utils.logLabeledWarning("functions", `Runtime ${exports.RUNTIMES[runtime].friendly} was deprecated on ${deprecationDate} and will be ` +
|
|
120
|
+
`decommissioned on ${decommissionDate}, after which you will not be able ` +
|
|
121
|
+
"to deploy without upgrading. Consider upgrading now to avoid disruption. See " +
|
|
122
|
+
"https://cloud.google.com/functions/docs/runtime-support for full " +
|
|
123
|
+
"details on the lifecycle policy");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const warning = new Date(deprecation.getTime() - 90 * 24 * 60 * 60 * 1000);
|
|
127
|
+
if (now >= warning) {
|
|
128
|
+
utils.logLabeledWarning("functions", `Runtime ${exports.RUNTIMES[runtime].friendly} will be deprecated on ${deprecationDate} and will be ` +
|
|
129
|
+
`decommissioned on ${decommissionDate}, after which you will not be able ` +
|
|
130
|
+
"to deploy without upgrading. Consider upgrading now to avoid disruption. See " +
|
|
131
|
+
"https://cloud.google.com/functions/docs/runtime-support for full " +
|
|
132
|
+
"details on the lifecycle policy");
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
exports.guardVersionSupport = guardVersionSupport;
|
|
@@ -104,6 +104,10 @@ const EVENT_SERVICE_MAPPING = {
|
|
|
104
104
|
"google.cloud.firestore.document.v1.created": firestoreService,
|
|
105
105
|
"google.cloud.firestore.document.v1.updated": firestoreService,
|
|
106
106
|
"google.cloud.firestore.document.v1.deleted": firestoreService,
|
|
107
|
+
"google.cloud.firestore.document.v1.written.withAuthContext": firestoreService,
|
|
108
|
+
"google.cloud.firestore.document.v1.created.withAuthContext": firestoreService,
|
|
109
|
+
"google.cloud.firestore.document.v1.updated.withAuthContext": firestoreService,
|
|
110
|
+
"google.cloud.firestore.document.v1.deleted.withAuthContext": firestoreService,
|
|
107
111
|
};
|
|
108
112
|
function serviceForEndpoint(endpoint) {
|
|
109
113
|
if (backend.isEventTriggered(endpoint)) {
|
|
@@ -41,6 +41,7 @@ const downloadableEmulators_1 = require("./downloadableEmulators");
|
|
|
41
41
|
const frameworks_1 = require("../frameworks");
|
|
42
42
|
const experiments = require("../experiments");
|
|
43
43
|
const portUtils_1 = require("./portUtils");
|
|
44
|
+
const supported_1 = require("../deploy/functions/runtimes/supported");
|
|
44
45
|
const START_LOGGING_EMULATOR = utils.envOverride("START_LOGGING_EMULATOR", "false", (val) => val === "true");
|
|
45
46
|
async function exportOnExit(options) {
|
|
46
47
|
const exportOnExitDir = options.exportOnExit;
|
|
@@ -308,7 +309,13 @@ async function startAll(options, showUI = true, runningTestScript = false) {
|
|
|
308
309
|
utils.assertIsStringOrUndefined(options.extDevDir);
|
|
309
310
|
for (const cfg of functionsCfg) {
|
|
310
311
|
const functionsDir = path.join(projectDir, cfg.source);
|
|
311
|
-
|
|
312
|
+
let runtime = ((_e = options.extDevRuntime) !== null && _e !== void 0 ? _e : cfg.runtime);
|
|
313
|
+
if (!runtime) {
|
|
314
|
+
runtime = (0, supported_1.latest)("nodejs");
|
|
315
|
+
}
|
|
316
|
+
if (!(0, supported_1.isRuntime)(runtime)) {
|
|
317
|
+
throw new error_1.FirebaseError(`Cannot load functions from ${functionsDir} because it has invalid runtime ${runtime}`);
|
|
318
|
+
}
|
|
312
319
|
emulatableBackends.push({
|
|
313
320
|
functionsDir,
|
|
314
321
|
runtime,
|
|
@@ -297,9 +297,9 @@ class FunctionsEmulator {
|
|
|
297
297
|
runtime: emulatableBackend.runtime,
|
|
298
298
|
};
|
|
299
299
|
const runtimeDelegate = await runtimes.getRuntimeDelegate(runtimeDelegateContext);
|
|
300
|
-
logger_1.logger.debug(`Validating ${runtimeDelegate.
|
|
300
|
+
logger_1.logger.debug(`Validating ${runtimeDelegate.language} source`);
|
|
301
301
|
await runtimeDelegate.validate();
|
|
302
|
-
logger_1.logger.debug(`Building ${runtimeDelegate.
|
|
302
|
+
logger_1.logger.debug(`Building ${runtimeDelegate.language} source`);
|
|
303
303
|
await runtimeDelegate.build();
|
|
304
304
|
emulatableBackend.runtime = runtimeDelegate.runtime;
|
|
305
305
|
emulatableBackend.bin = runtimeDelegate.bin;
|
package/lib/emulator/hub.js
CHANGED
|
@@ -53,6 +53,11 @@ class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
|
|
|
53
53
|
res.json(body);
|
|
54
54
|
});
|
|
55
55
|
app.post(EmulatorHub.PATH_EXPORT, async (req, res) => {
|
|
56
|
+
if (req.headers.origin) {
|
|
57
|
+
res.status(403).json({
|
|
58
|
+
message: `Export cannot be triggered by external callers.`,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
56
61
|
const path = req.body.path;
|
|
57
62
|
const initiatedBy = req.body.initiatedBy || "unknown";
|
|
58
63
|
utils.logLabeledBullet("emulators", `Received export request. Exporting data to ${path}.`);
|
package/lib/experiments.js
CHANGED
|
@@ -39,6 +39,18 @@ exports.ALL_EXPERIMENTS = experiments({
|
|
|
39
39
|
"of how that image was created.",
|
|
40
40
|
public: true,
|
|
41
41
|
},
|
|
42
|
+
automaticallydeletegcfartifacts: {
|
|
43
|
+
shortDescription: "Control whether functions cleans up images after deploys",
|
|
44
|
+
fullDescription: "To control costs, Firebase defaults to automatically deleting containers " +
|
|
45
|
+
"created during the build process. This has the side-effect of preventing " +
|
|
46
|
+
"users from rolling back to previous revisions using the Run API. To change " +
|
|
47
|
+
`this behavior, call ${(0, colorette_1.bold)("experiments:disable deletegcfartifactsondeploy")} ` +
|
|
48
|
+
`consider also calling ${(0, colorette_1.bold)("experiments:enable deletegcfartifacts")} ` +
|
|
49
|
+
`to enable the new command ${(0, colorette_1.bold)("functions:deletegcfartifacts")} which` +
|
|
50
|
+
"lets you clean up images manually",
|
|
51
|
+
public: true,
|
|
52
|
+
default: true,
|
|
53
|
+
},
|
|
42
54
|
emulatoruisnapshot: {
|
|
43
55
|
shortDescription: "Load pre-release versions of the emulator UI",
|
|
44
56
|
},
|