firebase-tools 9.20.0 → 9.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -1
- package/lib/api.js +2 -0
- package/lib/apiv2.js +7 -4
- package/lib/commands/crashlytics-symbols-upload.js +2 -2
- package/lib/commands/deploy.js +9 -1
- package/lib/commands/ext-configure.js +1 -1
- package/lib/commands/ext-dev-deprecate.js +63 -0
- package/lib/commands/ext-dev-undeprecate.js +56 -0
- package/lib/commands/ext-dev-unpublish.js +10 -3
- package/lib/commands/ext-export.js +44 -0
- package/lib/commands/ext-install.js +1 -1
- package/lib/commands/ext-update.js +1 -1
- package/lib/commands/functions-delete.js +55 -42
- package/lib/commands/functions-list.js +11 -11
- package/lib/commands/index.js +6 -5
- package/lib/commands/init.js +3 -0
- package/lib/config.js +3 -2
- package/lib/deploy/extensions/args.js +2 -0
- package/lib/deploy/extensions/deploy.js +49 -0
- package/lib/deploy/extensions/deploymentSummary.js +52 -0
- package/lib/deploy/extensions/errors.js +31 -0
- package/lib/deploy/extensions/index.js +8 -0
- package/lib/deploy/extensions/params.js +39 -0
- package/lib/deploy/extensions/planner.js +94 -0
- package/lib/deploy/extensions/prepare.js +111 -0
- package/lib/deploy/extensions/release.js +43 -0
- package/lib/deploy/extensions/secrets.js +150 -0
- package/lib/deploy/extensions/tasks.js +98 -0
- package/lib/deploy/extensions/validate.js +17 -0
- package/lib/deploy/functions/backend.js +84 -115
- package/lib/deploy/functions/checkIam.js +73 -12
- package/lib/deploy/functions/containerCleaner.js +97 -50
- package/lib/deploy/functions/deploy.js +4 -10
- package/lib/deploy/functions/eventTypes.js +10 -0
- package/lib/deploy/functions/functionsDeployHelper.js +3 -68
- package/lib/deploy/functions/prepare.js +72 -29
- package/lib/deploy/functions/pricing.js +17 -17
- package/lib/deploy/functions/prompts.js +22 -21
- package/lib/deploy/functions/release/executor.js +39 -0
- package/lib/deploy/functions/release/fabricator.js +425 -0
- package/lib/deploy/functions/release/index.js +73 -0
- package/lib/deploy/functions/release/planner.js +162 -0
- package/lib/deploy/functions/release/reporter.js +165 -0
- package/lib/deploy/functions/release/sourceTokenScraper.js +28 -0
- package/lib/deploy/functions/release/timer.js +14 -0
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +129 -126
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +32 -54
- package/lib/deploy/functions/services/index.js +38 -0
- package/lib/deploy/functions/services/storage.js +43 -0
- package/lib/deploy/functions/triggerRegionHelper.js +9 -25
- package/lib/deploy/functions/validate.js +1 -24
- package/lib/deploy/index.js +10 -1
- package/lib/emulator/auth/apiSpec.js +37 -6
- package/lib/emulator/auth/operations.js +45 -17
- package/lib/emulator/auth/server.js +16 -2
- package/lib/emulator/auth/state.js +34 -15
- package/lib/emulator/auth/widget_ui.js +14 -0
- package/lib/emulator/downloadableEmulators.js +7 -7
- package/lib/emulator/functionsEmulator.js +18 -4
- package/lib/emulator/storage/cloudFunctions.js +37 -7
- package/lib/ensureApiEnabled.js +10 -12
- package/lib/extensions/askUserForParam.js +14 -6
- package/lib/extensions/checkProjectBilling.js +7 -7
- package/lib/extensions/export.js +107 -0
- package/lib/extensions/extensionsApi.js +103 -21
- package/lib/extensions/extensionsHelper.js +5 -2
- package/lib/extensions/listExtensions.js +16 -11
- package/lib/extensions/paramHelper.js +6 -4
- package/lib/extensions/provisioningHelper.js +16 -3
- package/lib/extensions/refs.js +9 -1
- package/lib/extensions/secretsUtils.js +10 -9
- package/lib/extensions/updateHelper.js +12 -2
- package/lib/extensions/versionHelper.js +14 -0
- package/lib/extensions/warnings.js +33 -1
- package/lib/gcp/artifactregistry.js +16 -0
- package/lib/gcp/cloudfunctions.js +25 -72
- package/lib/gcp/cloudfunctionsv2.js +46 -98
- package/lib/gcp/cloudscheduler.js +22 -16
- package/lib/gcp/cloudtasks.js +143 -0
- package/lib/gcp/docker.js +36 -2
- package/lib/gcp/location.js +44 -0
- package/lib/gcp/proto.js +2 -2
- package/lib/gcp/pubsub.js +1 -9
- package/lib/gcp/secretManager.js +27 -6
- package/lib/gcp/storage.js +48 -32
- package/lib/init/features/project.js +2 -1
- package/lib/previews.js +1 -1
- package/lib/projectUtils.js +10 -1
- package/lib/utils.js +30 -1
- package/package.json +5 -4
- package/schema/firebase-config.json +9 -0
- package/lib/deploy/functions/deploymentPlanner.js +0 -113
- package/lib/deploy/functions/deploymentTimer.js +0 -23
- package/lib/deploy/functions/errorHandler.js +0 -75
- package/lib/deploy/functions/release.js +0 -116
- package/lib/deploy/functions/tasks.js +0 -324
- package/lib/functions/listFunctions.js +0 -10
- package/lib/functionsDelete.js +0 -60
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"additionalProperties": false,
|
|
4
|
+
"definitions": {
|
|
5
|
+
"ExtensionsConfig": {
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"type": "object"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
4
10
|
"properties": {
|
|
5
11
|
"database": {
|
|
6
12
|
"anyOf": [
|
|
@@ -273,6 +279,9 @@
|
|
|
273
279
|
},
|
|
274
280
|
"type": "object"
|
|
275
281
|
},
|
|
282
|
+
"extensions": {
|
|
283
|
+
"$ref": "#/definitions/ExtensionsConfig"
|
|
284
|
+
},
|
|
276
285
|
"firestore": {
|
|
277
286
|
"additionalProperties": false,
|
|
278
287
|
"properties": {
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createDeploymentPlan = exports.calculateRegionalFunctionChanges = exports.allRegions = exports.functionsByRegion = void 0;
|
|
4
|
-
const functionsDeployHelper_1 = require("./functionsDeployHelper");
|
|
5
|
-
const validate_1 = require("./validate");
|
|
6
|
-
const deploymentTool_1 = require("../../deploymentTool");
|
|
7
|
-
const utils_1 = require("../../utils");
|
|
8
|
-
const backend = require("./backend");
|
|
9
|
-
const gcfv2 = require("../../gcp/cloudfunctionsv2");
|
|
10
|
-
function functionsByRegion(allFunctions) {
|
|
11
|
-
const partitioned = {};
|
|
12
|
-
for (const fn of allFunctions) {
|
|
13
|
-
partitioned[fn.region] = partitioned[fn.region] || [];
|
|
14
|
-
partitioned[fn.region].push(fn);
|
|
15
|
-
}
|
|
16
|
-
return partitioned;
|
|
17
|
-
}
|
|
18
|
-
exports.functionsByRegion = functionsByRegion;
|
|
19
|
-
function allRegions(spec, existing) {
|
|
20
|
-
return Object.keys(Object.assign(Object.assign({}, spec), existing));
|
|
21
|
-
}
|
|
22
|
-
exports.allRegions = allRegions;
|
|
23
|
-
const matchesId = (hasId) => (test) => {
|
|
24
|
-
return hasId.id === test.id;
|
|
25
|
-
};
|
|
26
|
-
function calculateRegionalFunctionChanges(want, have, options) {
|
|
27
|
-
want = want.filter((fn) => functionsDeployHelper_1.functionMatchesAnyGroup(fn, options.filters));
|
|
28
|
-
have = have.filter((fn) => functionsDeployHelper_1.functionMatchesAnyGroup(fn, options.filters));
|
|
29
|
-
let upgradedToGCFv2WithoutSettingConcurrency = false;
|
|
30
|
-
const functionsToCreate = want.filter((fn) => !have.some(matchesId(fn)));
|
|
31
|
-
const functionsToUpdate = want
|
|
32
|
-
.filter((fn) => {
|
|
33
|
-
const haveFn = have.find(matchesId(fn));
|
|
34
|
-
if (!haveFn) {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
validate_1.checkForInvalidChangeOfTrigger(fn, haveFn);
|
|
38
|
-
if (!options.overwriteEnvs) {
|
|
39
|
-
fn.environmentVariables = Object.assign(Object.assign({}, haveFn.environmentVariables), fn.environmentVariables);
|
|
40
|
-
}
|
|
41
|
-
if (haveFn.platform === "gcfv1" && fn.platform === "gcfv2" && !fn.concurrency) {
|
|
42
|
-
upgradedToGCFv2WithoutSettingConcurrency = true;
|
|
43
|
-
}
|
|
44
|
-
return true;
|
|
45
|
-
})
|
|
46
|
-
.map((fn) => {
|
|
47
|
-
const haveFn = have.find(matchesId(fn));
|
|
48
|
-
const deleteAndRecreate = needsDeleteAndRecreate(haveFn, fn);
|
|
49
|
-
return {
|
|
50
|
-
func: fn,
|
|
51
|
-
deleteAndRecreate,
|
|
52
|
-
};
|
|
53
|
-
});
|
|
54
|
-
const functionsToDelete = have
|
|
55
|
-
.filter((fn) => !want.some(matchesId(fn)))
|
|
56
|
-
.filter((fn) => deploymentTool_1.isFirebaseManaged(fn.labels || {}));
|
|
57
|
-
if (upgradedToGCFv2WithoutSettingConcurrency) {
|
|
58
|
-
utils_1.logLabeledBullet("functions", "You are updating one or more functions to Google Cloud Functions v2, " +
|
|
59
|
-
"which introduces support for concurrent execution. New functions " +
|
|
60
|
-
"default to 80 concurrent executions, but existing functions keep the " +
|
|
61
|
-
"old default of 1. You can change this with the 'concurrency' option.");
|
|
62
|
-
}
|
|
63
|
-
return { functionsToCreate, functionsToUpdate, functionsToDelete };
|
|
64
|
-
}
|
|
65
|
-
exports.calculateRegionalFunctionChanges = calculateRegionalFunctionChanges;
|
|
66
|
-
function createDeploymentPlan(want, have, options) {
|
|
67
|
-
const deployment = {
|
|
68
|
-
regionalDeployments: {},
|
|
69
|
-
schedulesToUpsert: [],
|
|
70
|
-
schedulesToDelete: [],
|
|
71
|
-
topicsToDelete: [],
|
|
72
|
-
};
|
|
73
|
-
const wantRegionalFunctions = functionsByRegion(want.cloudFunctions);
|
|
74
|
-
const haveRegionalFunctions = functionsByRegion(have.cloudFunctions);
|
|
75
|
-
for (const region of allRegions(wantRegionalFunctions, haveRegionalFunctions)) {
|
|
76
|
-
const want = wantRegionalFunctions[region] || [];
|
|
77
|
-
const have = haveRegionalFunctions[region] || [];
|
|
78
|
-
deployment.regionalDeployments[region] = calculateRegionalFunctionChanges(want, have, options);
|
|
79
|
-
}
|
|
80
|
-
deployment.schedulesToUpsert = want.schedules.filter((schedule) => functionsDeployHelper_1.functionMatchesAnyGroup(schedule.targetService, options.filters));
|
|
81
|
-
deployment.schedulesToDelete = have.schedules
|
|
82
|
-
.filter((schedule) => !want.schedules.some(matchesId(schedule)))
|
|
83
|
-
.filter((schedule) => functionsDeployHelper_1.functionMatchesAnyGroup(schedule.targetService, options.filters));
|
|
84
|
-
deployment.topicsToDelete = have.topics
|
|
85
|
-
.filter((topic) => !want.topics.some(matchesId(topic)))
|
|
86
|
-
.filter((topic) => functionsDeployHelper_1.functionMatchesAnyGroup(topic.targetService, options.filters));
|
|
87
|
-
return deployment;
|
|
88
|
-
}
|
|
89
|
-
exports.createDeploymentPlan = createDeploymentPlan;
|
|
90
|
-
function needsDeleteAndRecreate(exFn, fn) {
|
|
91
|
-
return changedV2PubSubTopic(exFn, fn);
|
|
92
|
-
}
|
|
93
|
-
function changedV2PubSubTopic(exFn, fn) {
|
|
94
|
-
if (exFn.platform !== "gcfv2") {
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
if (fn.platform !== "gcfv2") {
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
if (!backend.isEventTrigger(exFn.trigger)) {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
if (!backend.isEventTrigger(fn.trigger)) {
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
if (exFn.trigger.eventType !== gcfv2.PUBSUB_PUBLISH_EVENT) {
|
|
107
|
-
return false;
|
|
108
|
-
}
|
|
109
|
-
if (fn.trigger.eventType != gcfv2.PUBSUB_PUBLISH_EVENT) {
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
return exFn.trigger.eventFilters["resource"] != fn.trigger.eventFilters["resource"];
|
|
113
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DeploymentTimer = void 0;
|
|
4
|
-
const logger_1 = require("../../logger");
|
|
5
|
-
const track = require("../../track");
|
|
6
|
-
class DeploymentTimer {
|
|
7
|
-
constructor() {
|
|
8
|
-
this.timings = {};
|
|
9
|
-
}
|
|
10
|
-
startTimer(name, type) {
|
|
11
|
-
this.timings[name] = { type: type, t0: process.hrtime() };
|
|
12
|
-
}
|
|
13
|
-
endTimer(name) {
|
|
14
|
-
if (!this.timings[name]) {
|
|
15
|
-
logger_1.logger.debug("[functions] no timer initialized for", name);
|
|
16
|
-
return 0;
|
|
17
|
-
}
|
|
18
|
-
const duration = process.hrtime(this.timings[name].t0);
|
|
19
|
-
track("Functions Deploy (Duration)", this.timings[name].type, duration[0] * 1000 + Math.round(duration[1] * 1e-6));
|
|
20
|
-
return duration[0] * 1000 * Math.round(duration[1] * 1e-6);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
exports.DeploymentTimer = DeploymentTimer;
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ErrorHandler = void 0;
|
|
4
|
-
const clc = require("cli-color");
|
|
5
|
-
const logger_1 = require("../../logger");
|
|
6
|
-
const functionsDeployHelper_1 = require("./functionsDeployHelper");
|
|
7
|
-
const error_1 = require("../../error");
|
|
8
|
-
class ErrorHandler {
|
|
9
|
-
constructor() {
|
|
10
|
-
this.errors = [];
|
|
11
|
-
this.warnings = [];
|
|
12
|
-
}
|
|
13
|
-
record(level, functionName, operationType, message) {
|
|
14
|
-
const info = {
|
|
15
|
-
functionName,
|
|
16
|
-
operationType,
|
|
17
|
-
message,
|
|
18
|
-
};
|
|
19
|
-
if (level === "error") {
|
|
20
|
-
this.errors.push(info);
|
|
21
|
-
}
|
|
22
|
-
else if (level === "warning") {
|
|
23
|
-
this.warnings.push(info);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
printErrors() {
|
|
27
|
-
if (this.errors.length === 0) {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
logger_1.logger.info("");
|
|
31
|
-
logger_1.logger.info("Functions deploy had errors with the following functions:");
|
|
32
|
-
for (const failedDeployment of this.errors) {
|
|
33
|
-
logger_1.logger.info(`\t${functionsDeployHelper_1.getFunctionLabel(failedDeployment.functionName)}`);
|
|
34
|
-
}
|
|
35
|
-
const failedIamCalls = this.errors.filter((e) => e.operationType === "set invoker");
|
|
36
|
-
if (failedIamCalls.length) {
|
|
37
|
-
logger_1.logger.info("");
|
|
38
|
-
logger_1.logger.info("Unable to set the invoker for the IAM policy on the following functions:");
|
|
39
|
-
for (const failedDep of failedIamCalls) {
|
|
40
|
-
logger_1.logger.info(`\t${failedDep.functionName}`);
|
|
41
|
-
}
|
|
42
|
-
logger_1.logger.info("");
|
|
43
|
-
logger_1.logger.info("Some common causes of this:");
|
|
44
|
-
logger_1.logger.info("");
|
|
45
|
-
logger_1.logger.info("- You may not have the roles/functions.admin IAM role. Note that roles/functions.developer does not allow you to change IAM policies.");
|
|
46
|
-
logger_1.logger.info("");
|
|
47
|
-
logger_1.logger.info("- An organization policy that restricts Network Access on your project.");
|
|
48
|
-
}
|
|
49
|
-
logger_1.logger.info("");
|
|
50
|
-
logger_1.logger.info("To try redeploying those functions, run:");
|
|
51
|
-
logger_1.logger.info(" " +
|
|
52
|
-
clc.bold("firebase deploy --only ") +
|
|
53
|
-
clc.bold('"') +
|
|
54
|
-
clc.bold(this.errors
|
|
55
|
-
.map((failedDeployment) => `functions:${functionsDeployHelper_1.getFunctionId(failedDeployment.functionName).replace(/-/g, ".")}`)
|
|
56
|
-
.join(",")) +
|
|
57
|
-
clc.bold('"'));
|
|
58
|
-
logger_1.logger.info("");
|
|
59
|
-
logger_1.logger.info("To continue deploying other features (such as database), run:");
|
|
60
|
-
logger_1.logger.info(" " + clc.bold("firebase deploy --except functions"));
|
|
61
|
-
for (const failedDeployment of this.errors) {
|
|
62
|
-
logger_1.logger.debug(`\tError during ${failedDeployment.operationType} for ${failedDeployment.functionName}: ${failedDeployment.message}`);
|
|
63
|
-
}
|
|
64
|
-
throw new error_1.FirebaseError("Functions did not deploy properly.");
|
|
65
|
-
}
|
|
66
|
-
printWarnings() {
|
|
67
|
-
if (this.warnings.length === 0) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
for (const failedDeployment of this.warnings) {
|
|
71
|
-
logger_1.logger.debug(`\tWarning during${failedDeployment.operationType} for ${failedDeployment.functionName}: ${failedDeployment.message}`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
exports.ErrorHandler = ErrorHandler;
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.release = void 0;
|
|
4
|
-
const queue_1 = require("../../throttler/queue");
|
|
5
|
-
const deploymentPlanner_1 = require("./deploymentPlanner");
|
|
6
|
-
const functionsConfig_1 = require("../../functionsConfig");
|
|
7
|
-
const env_1 = require("../../functions/env");
|
|
8
|
-
const prompts_1 = require("./prompts");
|
|
9
|
-
const deploymentTimer_1 = require("./deploymentTimer");
|
|
10
|
-
const errorHandler_1 = require("./errorHandler");
|
|
11
|
-
const previews_1 = require("../../previews");
|
|
12
|
-
const backend = require("./backend");
|
|
13
|
-
const containerCleaner = require("./containerCleaner");
|
|
14
|
-
const helper = require("./functionsDeployHelper");
|
|
15
|
-
const tasks = require("./tasks");
|
|
16
|
-
const utils = require("../../utils");
|
|
17
|
-
const track_1 = require("../../track");
|
|
18
|
-
async function release(context, options, payload) {
|
|
19
|
-
if (!options.config.has("functions")) {
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
const projectId = context.projectId;
|
|
23
|
-
const sourceUrl = context.uploadUrl;
|
|
24
|
-
const functionsSource = options.config.get("functions.source");
|
|
25
|
-
const appEngineLocation = functionsConfig_1.getAppEngineLocation(context.firebaseConfig);
|
|
26
|
-
const timer = new deploymentTimer_1.DeploymentTimer();
|
|
27
|
-
const errorHandler = new errorHandler_1.ErrorHandler();
|
|
28
|
-
const fullDeployment = deploymentPlanner_1.createDeploymentPlan(payload.functions.backend, await backend.existingBackend(context), {
|
|
29
|
-
filters: context.filters,
|
|
30
|
-
overwriteEnvs: previews_1.previews.dotenv &&
|
|
31
|
-
env_1.hasUserEnvs({
|
|
32
|
-
functionsSource: options.config.path(functionsSource),
|
|
33
|
-
projectId,
|
|
34
|
-
projectAlias: options.projectAlias,
|
|
35
|
-
}),
|
|
36
|
-
});
|
|
37
|
-
const cloudFunctionsQueue = new queue_1.default({
|
|
38
|
-
retries: 30,
|
|
39
|
-
backoff: 20000,
|
|
40
|
-
concurrency: 40,
|
|
41
|
-
maxBackoff: 40000,
|
|
42
|
-
handler: tasks.functionsDeploymentHandler(timer, errorHandler),
|
|
43
|
-
});
|
|
44
|
-
const schedulerQueue = new queue_1.default({
|
|
45
|
-
handler: tasks.schedulerDeploymentHandler(errorHandler),
|
|
46
|
-
});
|
|
47
|
-
const pubSubQueue = new queue_1.default({
|
|
48
|
-
handler: tasks.schedulerDeploymentHandler(errorHandler),
|
|
49
|
-
});
|
|
50
|
-
const regionPromises = [];
|
|
51
|
-
const taskParams = {
|
|
52
|
-
projectId,
|
|
53
|
-
sourceUrl,
|
|
54
|
-
storage: context.storage,
|
|
55
|
-
errorHandler,
|
|
56
|
-
};
|
|
57
|
-
const allFnsToDelete = Object.values(fullDeployment.regionalDeployments).reduce((accum, region) => [...accum, ...region.functionsToDelete], []);
|
|
58
|
-
const shouldDeleteFunctions = await prompts_1.promptForFunctionDeletion(allFnsToDelete, options.force, options.nonInteractive);
|
|
59
|
-
if (!shouldDeleteFunctions) {
|
|
60
|
-
fullDeployment.schedulesToDelete = fullDeployment.schedulesToDelete.filter((schedule) => {
|
|
61
|
-
return !allFnsToDelete.find(backend.sameFunctionName(schedule.targetService));
|
|
62
|
-
});
|
|
63
|
-
fullDeployment.topicsToDelete = fullDeployment.topicsToDelete.filter((topic) => {
|
|
64
|
-
return !allFnsToDelete.find(backend.sameFunctionName(topic.targetService));
|
|
65
|
-
});
|
|
66
|
-
for (const regionalDeployment of Object.values(fullDeployment.regionalDeployments)) {
|
|
67
|
-
regionalDeployment.functionsToDelete = [];
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
for (const [region, deployment] of Object.entries(fullDeployment.regionalDeployments)) {
|
|
71
|
-
regionPromises.push(tasks.runRegionalFunctionDeployment(taskParams, region, deployment, cloudFunctionsQueue));
|
|
72
|
-
}
|
|
73
|
-
for (const schedule of fullDeployment.schedulesToUpsert) {
|
|
74
|
-
const task = tasks.upsertScheduleTask(taskParams, schedule, appEngineLocation);
|
|
75
|
-
void schedulerQueue.run(task);
|
|
76
|
-
}
|
|
77
|
-
for (const schedule of fullDeployment.schedulesToDelete) {
|
|
78
|
-
const task = tasks.deleteScheduleTask(taskParams, schedule, appEngineLocation);
|
|
79
|
-
void schedulerQueue.run(task);
|
|
80
|
-
}
|
|
81
|
-
for (const topic of fullDeployment.topicsToDelete) {
|
|
82
|
-
const task = tasks.deleteTopicTask(taskParams, topic);
|
|
83
|
-
void pubSubQueue.run(task);
|
|
84
|
-
}
|
|
85
|
-
const queuePromises = [cloudFunctionsQueue.wait(), schedulerQueue.wait(), pubSubQueue.wait()];
|
|
86
|
-
cloudFunctionsQueue.process();
|
|
87
|
-
schedulerQueue.process();
|
|
88
|
-
pubSubQueue.process();
|
|
89
|
-
schedulerQueue.close();
|
|
90
|
-
pubSubQueue.close();
|
|
91
|
-
await Promise.all(regionPromises);
|
|
92
|
-
cloudFunctionsQueue.close();
|
|
93
|
-
try {
|
|
94
|
-
await Promise.all(queuePromises);
|
|
95
|
-
}
|
|
96
|
-
catch (err) {
|
|
97
|
-
utils.reject("Exceeded maximum retries while deploying functions. " +
|
|
98
|
-
"If you are deploying a large number of functions, " +
|
|
99
|
-
"please deploy your functions in batches by using the --only flag, " +
|
|
100
|
-
"and wait a few minutes before deploying again. " +
|
|
101
|
-
"Go to https://firebase.google.com/docs/cli/#partial_deploys to learn more.", {
|
|
102
|
-
original: err,
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
const functions = payload.functions.backend.cloudFunctions;
|
|
106
|
-
const gcfv1 = functions.find((fn) => fn.platform === "gcfv1");
|
|
107
|
-
const gcfv2 = functions.find((fn) => fn.platform === "gcfv2");
|
|
108
|
-
const tag = gcfv1 && gcfv2 ? "v1+v2" : gcfv1 ? "v1" : "v2";
|
|
109
|
-
track_1.track("functions_codebase_deploy", tag, functions.length);
|
|
110
|
-
helper.logAndTrackDeployStats(cloudFunctionsQueue, errorHandler);
|
|
111
|
-
await containerCleaner.cleanupBuildImages(payload.functions.backend.cloudFunctions);
|
|
112
|
-
await helper.printTriggerUrls(context, payload.functions.backend);
|
|
113
|
-
errorHandler.printWarnings();
|
|
114
|
-
errorHandler.printErrors();
|
|
115
|
-
}
|
|
116
|
-
exports.release = release;
|
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.schedulerDeploymentHandler = exports.deleteTopicTask = exports.deleteScheduleTask = exports.upsertScheduleTask = exports.runRegionalFunctionDeployment = exports.functionsDeploymentHandler = exports.deleteFunctionTask = exports.updateFunctionTask = exports.createFunctionTask = void 0;
|
|
4
|
-
const clc = require("cli-color");
|
|
5
|
-
const logger_1 = require("../../logger");
|
|
6
|
-
const operation_poller_1 = require("../../operation-poller");
|
|
7
|
-
const api_1 = require("../../api");
|
|
8
|
-
const runtimes_1 = require("./runtimes");
|
|
9
|
-
const backend = require("./backend");
|
|
10
|
-
const cloudscheduler = require("../../gcp/cloudscheduler");
|
|
11
|
-
const deploymentTool = require("../../deploymentTool");
|
|
12
|
-
const gcf = require("../../gcp/cloudfunctions");
|
|
13
|
-
const gcfV2 = require("../../gcp/cloudfunctionsv2");
|
|
14
|
-
const cloudrun = require("../../gcp/run");
|
|
15
|
-
const helper = require("./functionsDeployHelper");
|
|
16
|
-
const pubsub = require("../../gcp/pubsub");
|
|
17
|
-
const utils = require("../../utils");
|
|
18
|
-
const error_1 = require("../../error");
|
|
19
|
-
const track_1 = require("../../track");
|
|
20
|
-
const gcfV1PollerOptions = {
|
|
21
|
-
apiOrigin: api_1.functionsOrigin,
|
|
22
|
-
apiVersion: gcf.API_VERSION,
|
|
23
|
-
masterTimeout: 25 * 60 * 1000,
|
|
24
|
-
};
|
|
25
|
-
const gcfV2PollerOptions = {
|
|
26
|
-
apiOrigin: api_1.functionsV2Origin,
|
|
27
|
-
apiVersion: gcfV2.API_VERSION,
|
|
28
|
-
masterTimeout: 25 * 60 * 1000,
|
|
29
|
-
};
|
|
30
|
-
const pollerOptionsByPlatform = {
|
|
31
|
-
gcfv1: gcfV1PollerOptions,
|
|
32
|
-
gcfv2: gcfV2PollerOptions,
|
|
33
|
-
};
|
|
34
|
-
function createFunctionTask(params, fn, sourceToken, onPoll) {
|
|
35
|
-
const fnName = backend.functionName(fn);
|
|
36
|
-
const run = async () => {
|
|
37
|
-
var _a;
|
|
38
|
-
utils.logBullet(clc.bold.cyan("functions: ") +
|
|
39
|
-
"creating " +
|
|
40
|
-
runtimes_1.getHumanFriendlyRuntimeName(fn.runtime) +
|
|
41
|
-
" function " +
|
|
42
|
-
clc.bold(helper.getFunctionLabel(fn)) +
|
|
43
|
-
"...");
|
|
44
|
-
let op;
|
|
45
|
-
if (fn.platform === "gcfv1") {
|
|
46
|
-
const apiFunction = gcf.functionFromSpec(fn, params.sourceUrl);
|
|
47
|
-
if (sourceToken) {
|
|
48
|
-
apiFunction.sourceToken = sourceToken;
|
|
49
|
-
}
|
|
50
|
-
op = await gcf.createFunction(apiFunction);
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
const apiFunction = gcfV2.functionFromSpec(fn, params.storage[fn.region]);
|
|
54
|
-
if ((_a = apiFunction.eventTrigger) === null || _a === void 0 ? void 0 : _a.pubsubTopic) {
|
|
55
|
-
try {
|
|
56
|
-
await pubsub.getTopic(apiFunction.eventTrigger.pubsubTopic);
|
|
57
|
-
}
|
|
58
|
-
catch (err) {
|
|
59
|
-
if (err.status !== 404) {
|
|
60
|
-
throw new error_1.FirebaseError("Unexpected error looking for Pub/Sub topic", {
|
|
61
|
-
original: err,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
await pubsub.createTopic({
|
|
65
|
-
name: apiFunction.eventTrigger.pubsubTopic,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
op = await gcfV2.createFunction(apiFunction);
|
|
70
|
-
}
|
|
71
|
-
const cloudFunction = await operation_poller_1.pollOperation(Object.assign(Object.assign({}, pollerOptionsByPlatform[fn.platform]), { pollerName: `create-${fnName}`, operationResourceName: op.name, onPoll }));
|
|
72
|
-
if (!backend.isEventTrigger(fn.trigger)) {
|
|
73
|
-
const invoker = fn.trigger.invoker || ["public"];
|
|
74
|
-
if (invoker[0] !== "private") {
|
|
75
|
-
try {
|
|
76
|
-
if (fn.platform === "gcfv1") {
|
|
77
|
-
await gcf.setInvokerCreate(params.projectId, fnName, invoker);
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
const serviceName = cloudFunction.serviceConfig.service;
|
|
81
|
-
cloudrun.setInvokerCreate(params.projectId, serviceName, invoker);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
catch (err) {
|
|
85
|
-
params.errorHandler.record("error", fnName, "set invoker", err.message);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
if (fn.platform !== "gcfv1") {
|
|
90
|
-
await setConcurrency(cloudFunction.serviceConfig.service, fn.concurrency || 80);
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
return {
|
|
94
|
-
run,
|
|
95
|
-
data: fn,
|
|
96
|
-
operationType: "create",
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
exports.createFunctionTask = createFunctionTask;
|
|
100
|
-
function updateFunctionTask(params, fn, sourceToken, onPoll) {
|
|
101
|
-
const fnName = backend.functionName(fn);
|
|
102
|
-
const run = async () => {
|
|
103
|
-
var _a;
|
|
104
|
-
utils.logBullet(clc.bold.cyan("functions: ") +
|
|
105
|
-
"updating " +
|
|
106
|
-
runtimes_1.getHumanFriendlyRuntimeName(fn.runtime) +
|
|
107
|
-
" function " +
|
|
108
|
-
clc.bold(helper.getFunctionLabel(fn)) +
|
|
109
|
-
"...");
|
|
110
|
-
let opName;
|
|
111
|
-
if (fn.platform == "gcfv1") {
|
|
112
|
-
const apiFunction = gcf.functionFromSpec(fn, params.sourceUrl);
|
|
113
|
-
if (sourceToken) {
|
|
114
|
-
apiFunction.sourceToken = sourceToken;
|
|
115
|
-
}
|
|
116
|
-
opName = (await gcf.updateFunction(apiFunction)).name;
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
const apiFunction = gcfV2.functionFromSpec(fn, params.storage[fn.region]);
|
|
120
|
-
if ((_a = apiFunction.eventTrigger) === null || _a === void 0 ? void 0 : _a.pubsubTopic) {
|
|
121
|
-
delete apiFunction.eventTrigger.pubsubTopic;
|
|
122
|
-
}
|
|
123
|
-
opName = (await gcfV2.updateFunction(apiFunction)).name;
|
|
124
|
-
}
|
|
125
|
-
const pollerOptions = Object.assign(Object.assign({}, pollerOptionsByPlatform[fn.platform]), { pollerName: `update-${fnName}`, operationResourceName: opName, onPoll });
|
|
126
|
-
const cloudFunction = await operation_poller_1.pollOperation(pollerOptions);
|
|
127
|
-
if (!backend.isEventTrigger(fn.trigger) && fn.trigger.invoker) {
|
|
128
|
-
try {
|
|
129
|
-
if (fn.platform === "gcfv1") {
|
|
130
|
-
await gcf.setInvokerUpdate(params.projectId, fnName, fn.trigger.invoker);
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
const serviceName = cloudFunction.serviceConfig.service;
|
|
134
|
-
cloudrun.setInvokerUpdate(params.projectId, serviceName, fn.trigger.invoker);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
catch (err) {
|
|
138
|
-
params.errorHandler.record("error", fnName, "set invoker", err.message);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
if ("concurrency" in fn) {
|
|
142
|
-
if (fn.platform === "gcfv1") {
|
|
143
|
-
throw new error_1.FirebaseError("Precondition failed: GCFv1 does not support concurrency");
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
await setConcurrency(cloudFunction.serviceConfig.service, fn.concurrency || 80);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
return {
|
|
151
|
-
run,
|
|
152
|
-
data: fn,
|
|
153
|
-
operationType: "update",
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
exports.updateFunctionTask = updateFunctionTask;
|
|
157
|
-
function deleteFunctionTask(params, fn) {
|
|
158
|
-
const fnName = backend.functionName(fn);
|
|
159
|
-
const run = async () => {
|
|
160
|
-
utils.logBullet(clc.bold.cyan("functions: ") +
|
|
161
|
-
"deleting function " +
|
|
162
|
-
clc.bold(helper.getFunctionLabel(fnName)) +
|
|
163
|
-
"...");
|
|
164
|
-
let res;
|
|
165
|
-
if (fn.platform == "gcfv1") {
|
|
166
|
-
res = await gcf.deleteFunction(fnName);
|
|
167
|
-
}
|
|
168
|
-
else {
|
|
169
|
-
res = await gcfV2.deleteFunction(fnName);
|
|
170
|
-
}
|
|
171
|
-
const pollerOptions = Object.assign(Object.assign({}, pollerOptionsByPlatform[fn.platform]), { pollerName: `delete-${fnName}`, operationResourceName: res.name });
|
|
172
|
-
await operation_poller_1.pollOperation(pollerOptions);
|
|
173
|
-
};
|
|
174
|
-
return {
|
|
175
|
-
run,
|
|
176
|
-
data: fn,
|
|
177
|
-
operationType: "delete",
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
exports.deleteFunctionTask = deleteFunctionTask;
|
|
181
|
-
async function setConcurrency(name, concurrency) {
|
|
182
|
-
const err = null;
|
|
183
|
-
while (true) {
|
|
184
|
-
const service = await cloudrun.getService(name);
|
|
185
|
-
delete service.status;
|
|
186
|
-
delete service.spec.template.metadata.name;
|
|
187
|
-
service.spec.template.spec.containerConcurrency = concurrency;
|
|
188
|
-
try {
|
|
189
|
-
await cloudrun.replaceService(name, service);
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
catch (err) {
|
|
193
|
-
if (err.status !== 409) {
|
|
194
|
-
throw new error_1.FirebaseError("Unexpected error while trying to set concurrency", {
|
|
195
|
-
original: err,
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
function functionsDeploymentHandler(timer, errorHandler) {
|
|
202
|
-
return async (task) => {
|
|
203
|
-
var _a, _b, _c, _d;
|
|
204
|
-
let result;
|
|
205
|
-
const fnName = backend.functionName(task.data);
|
|
206
|
-
try {
|
|
207
|
-
timer.startTimer(fnName, task.operationType);
|
|
208
|
-
result = await task.run();
|
|
209
|
-
helper.printSuccess(task.data, task.operationType);
|
|
210
|
-
const duration = timer.endTimer(fnName);
|
|
211
|
-
track_1.track("function_deploy_success", backend.triggerTag(task.data), duration);
|
|
212
|
-
}
|
|
213
|
-
catch (err) {
|
|
214
|
-
if (((_c = (_b = (_a = err.original) === null || _a === void 0 ? void 0 : _a.context) === null || _b === void 0 ? void 0 : _b.response) === null || _c === void 0 ? void 0 : _c.statusCode) === 429) {
|
|
215
|
-
throw err;
|
|
216
|
-
}
|
|
217
|
-
errorHandler.record("error", fnName, task.operationType, ((_d = err.original) === null || _d === void 0 ? void 0 : _d.message) || "");
|
|
218
|
-
const duration = timer.endTimer(fnName);
|
|
219
|
-
track_1.track("function_deploy_failure", backend.triggerTag(task.data), duration);
|
|
220
|
-
}
|
|
221
|
-
return result;
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
exports.functionsDeploymentHandler = functionsDeploymentHandler;
|
|
225
|
-
async function runRegionalFunctionDeployment(params, region, regionalDeployment, queue) {
|
|
226
|
-
let resolveToken;
|
|
227
|
-
const getRealToken = new Promise((resolve) => (resolveToken = resolve));
|
|
228
|
-
let firstToken = true;
|
|
229
|
-
const getToken = () => {
|
|
230
|
-
if (firstToken) {
|
|
231
|
-
firstToken = false;
|
|
232
|
-
return Promise.resolve(undefined);
|
|
233
|
-
}
|
|
234
|
-
return getRealToken;
|
|
235
|
-
};
|
|
236
|
-
const onPollFn = (op) => {
|
|
237
|
-
var _a, _b;
|
|
238
|
-
if (((_a = op.metadata) === null || _a === void 0 ? void 0 : _a.sourceToken) || op.done) {
|
|
239
|
-
logger_1.logger.debug(`Got sourceToken ${op.metadata.sourceToken} for region ${region}`);
|
|
240
|
-
resolveToken((_b = op.metadata) === null || _b === void 0 ? void 0 : _b.sourceToken);
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
const deploy = async (functionSpec, createTask) => {
|
|
244
|
-
functionSpec.labels = Object.assign(Object.assign({}, (functionSpec.labels || {})), deploymentTool.labels());
|
|
245
|
-
let task;
|
|
246
|
-
if (functionSpec.platform == "gcfv2") {
|
|
247
|
-
task = createTask(params, functionSpec, undefined, () => undefined);
|
|
248
|
-
}
|
|
249
|
-
else {
|
|
250
|
-
const sourceToken = await getToken();
|
|
251
|
-
task = createTask(params, functionSpec, sourceToken, onPollFn);
|
|
252
|
-
}
|
|
253
|
-
return queue.run(task);
|
|
254
|
-
};
|
|
255
|
-
const deploys = [];
|
|
256
|
-
deploys.push(...regionalDeployment.functionsToCreate.map((fn) => deploy(fn, createFunctionTask)));
|
|
257
|
-
deploys.push(...regionalDeployment.functionsToUpdate.map(async (update) => {
|
|
258
|
-
if (update.deleteAndRecreate) {
|
|
259
|
-
await queue.run(deleteFunctionTask(params, update.func));
|
|
260
|
-
return deploy(update.func, createFunctionTask);
|
|
261
|
-
}
|
|
262
|
-
else {
|
|
263
|
-
return deploy(update.func, updateFunctionTask);
|
|
264
|
-
}
|
|
265
|
-
}));
|
|
266
|
-
await Promise.all(deploys);
|
|
267
|
-
const deletes = regionalDeployment.functionsToDelete.map(async (fn) => {
|
|
268
|
-
const task = deleteFunctionTask(params, fn);
|
|
269
|
-
await queue.run(task);
|
|
270
|
-
});
|
|
271
|
-
await Promise.all(deletes);
|
|
272
|
-
}
|
|
273
|
-
exports.runRegionalFunctionDeployment = runRegionalFunctionDeployment;
|
|
274
|
-
function upsertScheduleTask(params, schedule, appEngineLocation) {
|
|
275
|
-
const run = async () => {
|
|
276
|
-
const job = cloudscheduler.jobFromSpec(schedule, appEngineLocation);
|
|
277
|
-
await cloudscheduler.createOrReplaceJob(job);
|
|
278
|
-
};
|
|
279
|
-
return {
|
|
280
|
-
run,
|
|
281
|
-
data: schedule,
|
|
282
|
-
operationType: "upsert schedule",
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
exports.upsertScheduleTask = upsertScheduleTask;
|
|
286
|
-
function deleteScheduleTask(params, schedule, appEngineLocation) {
|
|
287
|
-
const run = async () => {
|
|
288
|
-
const jobName = backend.scheduleName(schedule, appEngineLocation);
|
|
289
|
-
await cloudscheduler.deleteJob(jobName);
|
|
290
|
-
};
|
|
291
|
-
return {
|
|
292
|
-
run,
|
|
293
|
-
data: schedule,
|
|
294
|
-
operationType: "delete schedule",
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
exports.deleteScheduleTask = deleteScheduleTask;
|
|
298
|
-
function deleteTopicTask(params, topic) {
|
|
299
|
-
const run = async () => {
|
|
300
|
-
const topicName = backend.topicName(topic);
|
|
301
|
-
await pubsub.deleteTopic(topicName);
|
|
302
|
-
};
|
|
303
|
-
return {
|
|
304
|
-
run,
|
|
305
|
-
data: topic,
|
|
306
|
-
operationType: "delete topic",
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
exports.deleteTopicTask = deleteTopicTask;
|
|
310
|
-
exports.schedulerDeploymentHandler = (errorHandler) => async (task) => {
|
|
311
|
-
try {
|
|
312
|
-
const result = await task.run();
|
|
313
|
-
helper.printSuccess(task.data.targetService, task.operationType);
|
|
314
|
-
return result;
|
|
315
|
-
}
|
|
316
|
-
catch (err) {
|
|
317
|
-
if (err.status === 429) {
|
|
318
|
-
throw err;
|
|
319
|
-
}
|
|
320
|
-
else if (err.status !== 404) {
|
|
321
|
-
errorHandler.record("error", backend.functionName(task.data.targetService), task.operationType, err.message || "");
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.listFunctions = void 0;
|
|
4
|
-
const backend = require("../deploy/functions/backend");
|
|
5
|
-
async function listFunctions(context) {
|
|
6
|
-
const functionSpecs = (await backend.existingBackend(context, true)).cloudFunctions;
|
|
7
|
-
functionSpecs.sort(backend.compareFunctions);
|
|
8
|
-
return { functions: functionSpecs };
|
|
9
|
-
}
|
|
10
|
-
exports.listFunctions = listFunctions;
|