firebase-tools 13.17.0 → 13.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -9
- package/lib/commands/dataconnect-services-list.js +4 -3
- package/lib/commands/dataconnect-sql-grant.js +37 -0
- package/lib/commands/dataconnect-sql-migrate.js +2 -1
- package/lib/commands/deploy.js +2 -0
- package/lib/commands/ext-info.js +3 -1
- package/lib/commands/ext-sdk-install.js +88 -0
- package/lib/commands/ext.js +1 -0
- package/lib/commands/index.js +3 -0
- package/lib/dataconnect/client.js +1 -1
- package/lib/dataconnect/dataplaneClient.js +1 -1
- package/lib/dataconnect/ensureApis.js +6 -1
- package/lib/dataconnect/load.js +3 -1
- package/lib/dataconnect/provisionCloudSql.js +34 -21
- package/lib/dataconnect/schemaMigration.js +126 -73
- package/lib/deploy/dataconnect/deploy.js +16 -13
- package/lib/deploy/dataconnect/prepare.js +36 -0
- package/lib/deploy/dataconnect/release.js +9 -2
- package/lib/deploy/extensions/deploymentSummary.js +3 -2
- package/lib/deploy/extensions/planner.js +32 -3
- package/lib/deploy/extensions/prepare.js +36 -64
- package/lib/deploy/extensions/release.js +11 -10
- package/lib/deploy/extensions/tasks.js +32 -21
- package/lib/deploy/firestore/prepare.js +10 -0
- package/lib/deploy/firestore/release.js +3 -6
- package/lib/deploy/functions/checkIam.js +7 -2
- package/lib/deploy/functions/ensure.js +10 -2
- package/lib/deploy/functions/prepare.js +10 -2
- package/lib/deploy/functions/runtimes/node/index.js +1 -1
- package/lib/deploy/index.js +9 -5
- package/lib/emulator/downloadableEmulators.js +9 -9
- package/lib/extensions/extensionsApi.js +3 -2
- package/lib/extensions/extensionsHelper.js +3 -3
- package/lib/extensions/localHelper.js +31 -0
- package/lib/extensions/runtimes/common.js +186 -38
- package/lib/extensions/runtimes/node.js +399 -0
- package/lib/extensions/types.js +10 -14
- package/lib/extensions/warnings.js +5 -2
- package/lib/firestore/checkDatabaseType.js +10 -3
- package/lib/frameworks/angular/index.js +15 -3
- package/lib/frameworks/next/utils.js +1 -1
- package/lib/gcp/cloudsql/permissions.js +6 -1
- package/lib/gcp/secretManager.js +12 -5
- package/lib/init/features/dataconnect/index.js +66 -53
- package/lib/init/features/firestore/index.js +20 -1
- package/lib/init/features/firestore/indexes.js +4 -4
- package/package.json +1 -1
- package/schema/connector-yaml.json +43 -17
- package/templates/init/dataconnect/connector.yaml +0 -1
- package/templates/init/dataconnect/dataconnect-fdccompatiblemode.yaml +12 -0
- package/templates/init/dataconnect/dataconnect.yaml +1 -1
|
@@ -17,20 +17,19 @@ const etags_1 = require("../../extensions/etags");
|
|
|
17
17
|
const v2FunctionHelper_1 = require("./v2FunctionHelper");
|
|
18
18
|
const tos_1 = require("../../extensions/tos");
|
|
19
19
|
const common_1 = require("../../extensions/runtimes/common");
|
|
20
|
-
const projectConfig_1 = require("../../functions/projectConfig");
|
|
21
20
|
const functionsDeployHelper_1 = require("../functions/functionsDeployHelper");
|
|
22
|
-
async function prepareHelper(context, options, payload, wantExtensions,
|
|
21
|
+
async function prepareHelper(context, options, payload, wantExtensions, haveExtensions, isDynamic) {
|
|
23
22
|
var _a, _b;
|
|
24
23
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
25
|
-
context.have = await planner.have(projectId);
|
|
26
24
|
context.want = wantExtensions;
|
|
25
|
+
context.have = haveExtensions;
|
|
27
26
|
const etagsChanged = (0, etags_1.detectEtagChanges)(options.rc, projectId, context.have);
|
|
28
27
|
if (etagsChanged.length) {
|
|
29
28
|
const wantChangedIds = wantExtensions
|
|
30
29
|
.map((e) => e.instanceId)
|
|
31
30
|
.filter((id) => etagsChanged.includes(id));
|
|
32
31
|
if (wantChangedIds.length) {
|
|
33
|
-
(0, warnings_1.outOfBandChangesWarning)(wantChangedIds);
|
|
32
|
+
(0, warnings_1.outOfBandChangesWarning)(wantChangedIds, isDynamic);
|
|
34
33
|
if (!(await prompt.confirm({
|
|
35
34
|
message: `Do you wish to continue deploying these extension instances?`,
|
|
36
35
|
default: false,
|
|
@@ -52,7 +51,7 @@ async function prepareHelper(context, options, payload, wantExtensions, noDelete
|
|
|
52
51
|
payload.instancesToCreate = context.want.filter((i) => { var _a; return !((_a = context.have) === null || _a === void 0 ? void 0 : _a.some(matchesInstanceId(i))); });
|
|
53
52
|
payload.instancesToConfigure = context.want.filter((i) => { var _a; return (_a = context.have) === null || _a === void 0 ? void 0 : _a.some(isConfigure(i)); });
|
|
54
53
|
payload.instancesToUpdate = context.want.filter((i) => { var _a; return (_a = context.have) === null || _a === void 0 ? void 0 : _a.some(isUpdate(i)); });
|
|
55
|
-
payload.instancesToDelete = context.have.filter((i) => { var _a; return !((_a = context.want) === null || _a === void 0 ? void 0 : _a.some(matchesInstanceId(i)))
|
|
54
|
+
payload.instancesToDelete = context.have.filter((i) => { var _a; return !((_a = context.want) === null || _a === void 0 ? void 0 : _a.some(matchesInstanceId(i))); });
|
|
56
55
|
if (await (0, warnings_1.displayWarningsForDeploy)(payload.instancesToCreate)) {
|
|
57
56
|
if (!(await prompt.confirm({
|
|
58
57
|
message: `Do you wish to continue deploying these extension instances?`,
|
|
@@ -64,9 +63,6 @@ async function prepareHelper(context, options, payload, wantExtensions, noDelete
|
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
const permissionsNeeded = [];
|
|
67
|
-
if (!isPrimaryCall) {
|
|
68
|
-
payload.instancesToDelete = [];
|
|
69
|
-
}
|
|
70
66
|
if (payload.instancesToCreate.length) {
|
|
71
67
|
permissionsNeeded.push("firebaseextensions.instances.create");
|
|
72
68
|
logger_1.logger.info(deploymentSummary.createsSummary(payload.instancesToCreate));
|
|
@@ -80,15 +76,20 @@ async function prepareHelper(context, options, payload, wantExtensions, noDelete
|
|
|
80
76
|
logger_1.logger.info(deploymentSummary.configuresSummary(payload.instancesToConfigure));
|
|
81
77
|
}
|
|
82
78
|
if (payload.instancesToDelete.length) {
|
|
83
|
-
logger_1.logger.info(deploymentSummary.deletesSummary(payload.instancesToDelete));
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
79
|
+
logger_1.logger.info(deploymentSummary.deletesSummary(payload.instancesToDelete, isDynamic));
|
|
80
|
+
if (options.dryRun) {
|
|
81
|
+
logger_1.logger.info("On your next deploy, you will be asked if you want to delete these instances.");
|
|
82
|
+
logger_1.logger.info("If you deploy --force, they will be deleted.");
|
|
83
|
+
}
|
|
84
|
+
if (!options.dryRun &&
|
|
85
|
+
!(await prompt.confirm({
|
|
86
|
+
message: `Would you like to delete ${payload.instancesToDelete
|
|
87
|
+
.map((i) => i.instanceId)
|
|
88
|
+
.join(", ")}?`,
|
|
89
|
+
default: false,
|
|
90
|
+
nonInteractive: options.nonInteractive,
|
|
91
|
+
force: options.force,
|
|
92
|
+
}))) {
|
|
92
93
|
payload.instancesToDelete = [];
|
|
93
94
|
}
|
|
94
95
|
else {
|
|
@@ -96,61 +97,36 @@ async function prepareHelper(context, options, payload, wantExtensions, noDelete
|
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
await (0, requirePermissions_1.requirePermissions)(options, permissionsNeeded);
|
|
99
|
-
|
|
100
|
+
if (options.dryRun) {
|
|
101
|
+
const appDevTos = await (0, tos_1.getAppDeveloperTOSStatus)(projectId);
|
|
102
|
+
if (!appDevTos.lastAcceptedVersion) {
|
|
103
|
+
logger_1.logger.info("On your next deploy, you will be asked to accept the Firebase Extensions App Developer Terms of Service");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
await (0, tos_1.acceptLatestAppDeveloperTOS)(options, projectId, context.want.map((i) => i.instanceId));
|
|
108
|
+
}
|
|
100
109
|
}
|
|
101
110
|
async function prepareDynamicExtensions(context, options, payload, builds) {
|
|
102
111
|
const filters = (0, functionsDeployHelper_1.getEndpointFilters)(options);
|
|
103
112
|
const extensions = (0, common_1.extractExtensionsFromBuilds)(builds, filters);
|
|
104
|
-
const isApiEnabled = await (0, extensionsHelper_1.checkExtensionsApiEnabled)(options);
|
|
105
|
-
if (Object.keys(extensions).length === 0 && !isApiEnabled) {
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
113
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
109
114
|
const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
|
|
110
|
-
const aliases = (0, projectUtils_1.getAliases)(options, projectId);
|
|
111
|
-
const projectDir = options.config.projectDir;
|
|
112
|
-
const isPrimaryCall = !!options.only && !options.only.split(",").includes("extensions");
|
|
113
115
|
await (0, extensionsHelper_1.ensureExtensionsApiEnabled)(options);
|
|
114
116
|
await (0, requirePermissions_1.requirePermissions)(options, ["firebaseextensions.instances.list"]);
|
|
117
|
+
let haveExtensions = await planner.haveDynamic(projectId);
|
|
118
|
+
haveExtensions = haveExtensions.filter((e) => { var _a; return (0, common_1.extensionMatchesAnyFilter)((_a = e.labels) === null || _a === void 0 ? void 0 : _a.codebase, e.instanceId, filters); });
|
|
119
|
+
if (Object.keys(extensions).length === 0 && haveExtensions.length === 0) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
115
122
|
const dynamicWant = await planner.wantDynamic({
|
|
116
123
|
projectId,
|
|
117
124
|
projectNumber,
|
|
118
125
|
extensions,
|
|
119
126
|
});
|
|
120
|
-
|
|
121
|
-
if (isPrimaryCall) {
|
|
122
|
-
const firebaseJsonWant = await planner.want({
|
|
123
|
-
projectId,
|
|
124
|
-
projectNumber,
|
|
125
|
-
aliases,
|
|
126
|
-
projectDir,
|
|
127
|
-
extensions: options.config.get("extensions", {}),
|
|
128
|
-
});
|
|
129
|
-
noDeleteExtensions = noDeleteExtensions.concat(firebaseJsonWant);
|
|
130
|
-
if (hasNonDeployingCodebases(options)) {
|
|
131
|
-
const dynamicAll = await planner.wantDynamic({
|
|
132
|
-
projectId,
|
|
133
|
-
projectNumber,
|
|
134
|
-
extensions: await (0, common_1.extractAllDynamicExtensions)(options),
|
|
135
|
-
});
|
|
136
|
-
noDeleteExtensions = noDeleteExtensions.concat(dynamicAll);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return prepareHelper(context, options, payload, dynamicWant, noDeleteExtensions, isPrimaryCall);
|
|
127
|
+
return prepareHelper(context, options, payload, dynamicWant, haveExtensions, true);
|
|
140
128
|
}
|
|
141
129
|
exports.prepareDynamicExtensions = prepareDynamicExtensions;
|
|
142
|
-
function hasNonDeployingCodebases(options) {
|
|
143
|
-
const functionFilters = (0, functionsDeployHelper_1.getEndpointFilters)(options);
|
|
144
|
-
if (functionFilters === null || functionFilters === void 0 ? void 0 : functionFilters.length) {
|
|
145
|
-
return true;
|
|
146
|
-
}
|
|
147
|
-
const functionsConfig = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions);
|
|
148
|
-
const allCodebases = (0, functionsDeployHelper_1.targetCodebases)(functionsConfig);
|
|
149
|
-
const deployingCodebases = (0, functionsDeployHelper_1.targetCodebases)(functionsConfig, functionFilters);
|
|
150
|
-
if (allCodebases.length > deployingCodebases.length) {
|
|
151
|
-
return true;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
130
|
async function prepare(context, options, payload) {
|
|
155
131
|
context.extensionsStartTime = Date.now();
|
|
156
132
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
@@ -159,19 +135,15 @@ async function prepare(context, options, payload) {
|
|
|
159
135
|
const projectDir = options.config.projectDir;
|
|
160
136
|
await (0, extensionsHelper_1.ensureExtensionsApiEnabled)(options);
|
|
161
137
|
await (0, requirePermissions_1.requirePermissions)(options, ["firebaseextensions.instances.list"]);
|
|
162
|
-
const
|
|
138
|
+
const wantExtensions = await planner.want({
|
|
163
139
|
projectId,
|
|
164
140
|
projectNumber,
|
|
165
141
|
aliases,
|
|
166
142
|
projectDir,
|
|
167
143
|
extensions: options.config.get("extensions", {}),
|
|
168
144
|
});
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
projectNumber,
|
|
172
|
-
extensions: await (0, common_1.extractAllDynamicExtensions)(options),
|
|
173
|
-
});
|
|
174
|
-
return prepareHelper(context, options, payload, firebaseJsonWant, dynamicWant, true);
|
|
145
|
+
const haveExtensions = await planner.have(projectId);
|
|
146
|
+
return prepareHelper(context, options, payload, wantExtensions, haveExtensions, false);
|
|
175
147
|
}
|
|
176
148
|
exports.prepare = prepare;
|
|
177
149
|
const matchesInstanceId = (dep) => (test) => {
|
|
@@ -24,20 +24,20 @@ async function release(context, options, payload) {
|
|
|
24
24
|
concurrency: 5,
|
|
25
25
|
handler: tasks.extensionsDeploymentHandler(errorHandler),
|
|
26
26
|
});
|
|
27
|
-
for (const
|
|
28
|
-
const task = tasks.
|
|
27
|
+
for (const inst of (_a = payload.instancesToConfigure) !== null && _a !== void 0 ? _a : []) {
|
|
28
|
+
const task = tasks.configureExtensionInstanceTask(projectId, inst);
|
|
29
29
|
void deploymentQueue.run(task);
|
|
30
30
|
}
|
|
31
|
-
for (const
|
|
32
|
-
const task = tasks.
|
|
31
|
+
for (const inst of (_b = payload.instancesToDelete) !== null && _b !== void 0 ? _b : []) {
|
|
32
|
+
const task = tasks.deleteExtensionInstanceTask(projectId, inst);
|
|
33
33
|
void deploymentQueue.run(task);
|
|
34
34
|
}
|
|
35
|
-
for (const
|
|
36
|
-
const task = tasks.
|
|
35
|
+
for (const inst of (_c = payload.instancesToCreate) !== null && _c !== void 0 ? _c : []) {
|
|
36
|
+
const task = tasks.createExtensionInstanceTask(projectId, inst);
|
|
37
37
|
void deploymentQueue.run(task);
|
|
38
38
|
}
|
|
39
|
-
for (const
|
|
40
|
-
const task = tasks.
|
|
39
|
+
for (const inst of (_d = payload.instancesToUpdate) !== null && _d !== void 0 ? _d : []) {
|
|
40
|
+
const task = tasks.updateExtensionInstanceTask(projectId, inst);
|
|
41
41
|
void deploymentQueue.run(task);
|
|
42
42
|
}
|
|
43
43
|
const deploymentPromise = deploymentQueue.wait();
|
|
@@ -53,8 +53,9 @@ async function release(context, options, payload) {
|
|
|
53
53
|
errors: (_o = errorHandler.errors.length) !== null && _o !== void 0 ? _o : 0,
|
|
54
54
|
interactive: options.nonInteractive ? "false" : "true",
|
|
55
55
|
}, duration);
|
|
56
|
-
const
|
|
57
|
-
|
|
56
|
+
const have = await planner.have(projectId);
|
|
57
|
+
const dynamicHave = await planner.haveDynamic(projectId);
|
|
58
|
+
(0, etags_1.saveEtags)(options.rc, projectId, have.concat(dynamicHave));
|
|
58
59
|
if (errorHandler.hasErrors()) {
|
|
59
60
|
errorHandler.print();
|
|
60
61
|
throw new error_1.FirebaseError(`Extensions deployment failed.`);
|
|
@@ -7,6 +7,7 @@ const extensionsApi = require("../../extensions/extensionsApi");
|
|
|
7
7
|
const extensionsHelper_1 = require("../../extensions/extensionsHelper");
|
|
8
8
|
const refs = require("../../extensions/refs");
|
|
9
9
|
const utils = require("../../utils");
|
|
10
|
+
const types_1 = require("../../extensions/types");
|
|
10
11
|
const isRetryable = (err) => err.status === 429 || err.status === 409;
|
|
11
12
|
function extensionsDeploymentHandler(errorHandler) {
|
|
12
13
|
return async (task) => {
|
|
@@ -27,34 +28,37 @@ function extensionsDeploymentHandler(errorHandler) {
|
|
|
27
28
|
exports.extensionsDeploymentHandler = extensionsDeploymentHandler;
|
|
28
29
|
function createExtensionInstanceTask(projectId, instanceSpec, validateOnly = false) {
|
|
29
30
|
const run = async () => {
|
|
31
|
+
if (!validateOnly) {
|
|
32
|
+
utils.logLabeledBullet("extensions", `Creating ${clc.bold(instanceSpec.instanceId)} extension instance`);
|
|
33
|
+
}
|
|
34
|
+
const createArgs = {
|
|
35
|
+
projectId,
|
|
36
|
+
instanceId: instanceSpec.instanceId,
|
|
37
|
+
params: instanceSpec.params,
|
|
38
|
+
systemParams: instanceSpec.systemParams,
|
|
39
|
+
allowedEventTypes: instanceSpec.allowedEventTypes,
|
|
40
|
+
eventarcChannel: instanceSpec.eventarcChannel,
|
|
41
|
+
validateOnly,
|
|
42
|
+
labels: instanceSpec.labels,
|
|
43
|
+
};
|
|
30
44
|
if (instanceSpec.ref) {
|
|
31
|
-
|
|
32
|
-
projectId,
|
|
33
|
-
instanceId: instanceSpec.instanceId,
|
|
34
|
-
params: instanceSpec.params,
|
|
35
|
-
systemParams: instanceSpec.systemParams,
|
|
36
|
-
extensionVersionRef: refs.toExtensionVersionRef(instanceSpec.ref),
|
|
37
|
-
allowedEventTypes: instanceSpec.allowedEventTypes,
|
|
38
|
-
eventarcChannel: instanceSpec.eventarcChannel,
|
|
39
|
-
validateOnly,
|
|
40
|
-
});
|
|
45
|
+
createArgs.extensionVersionRef = refs.toExtensionVersionRef(instanceSpec.ref);
|
|
41
46
|
}
|
|
42
47
|
else if (instanceSpec.localPath) {
|
|
43
|
-
|
|
44
|
-
await extensionsApi.createInstance({
|
|
45
|
-
projectId,
|
|
46
|
-
instanceId: instanceSpec.instanceId,
|
|
47
|
-
params: instanceSpec.params,
|
|
48
|
-
systemParams: instanceSpec.systemParams,
|
|
49
|
-
extensionSource,
|
|
50
|
-
allowedEventTypes: instanceSpec.allowedEventTypes,
|
|
51
|
-
eventarcChannel: instanceSpec.eventarcChannel,
|
|
52
|
-
validateOnly,
|
|
53
|
-
});
|
|
48
|
+
createArgs.extensionSource = await (0, extensionsHelper_1.createSourceFromLocation)(projectId, instanceSpec.localPath);
|
|
54
49
|
}
|
|
55
50
|
else {
|
|
56
51
|
throw new error_1.FirebaseError(`Tried to create extension instance ${instanceSpec.instanceId} without a ref or a local path. This should never happen.`);
|
|
57
52
|
}
|
|
53
|
+
try {
|
|
54
|
+
await extensionsApi.createInstance(createArgs);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
if ((0, types_1.isObject)(err) && err.status === 409) {
|
|
58
|
+
throw new error_1.FirebaseError(`Failed to create extension instance. Extension instance ${clc.bold(instanceSpec.instanceId)} already exists.`);
|
|
59
|
+
}
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
58
62
|
printSuccess(instanceSpec.instanceId, "create", validateOnly);
|
|
59
63
|
return;
|
|
60
64
|
};
|
|
@@ -67,6 +71,9 @@ function createExtensionInstanceTask(projectId, instanceSpec, validateOnly = fal
|
|
|
67
71
|
exports.createExtensionInstanceTask = createExtensionInstanceTask;
|
|
68
72
|
function updateExtensionInstanceTask(projectId, instanceSpec, validateOnly = false) {
|
|
69
73
|
const run = async () => {
|
|
74
|
+
if (!validateOnly) {
|
|
75
|
+
utils.logLabeledBullet("extensions", `Updating ${clc.bold(instanceSpec.instanceId)} extension instance`);
|
|
76
|
+
}
|
|
70
77
|
if (instanceSpec.ref) {
|
|
71
78
|
await extensionsApi.updateInstanceFromRegistry({
|
|
72
79
|
projectId,
|
|
@@ -109,6 +116,9 @@ function updateExtensionInstanceTask(projectId, instanceSpec, validateOnly = fal
|
|
|
109
116
|
exports.updateExtensionInstanceTask = updateExtensionInstanceTask;
|
|
110
117
|
function configureExtensionInstanceTask(projectId, instanceSpec, validateOnly = false) {
|
|
111
118
|
const run = async () => {
|
|
119
|
+
if (!validateOnly) {
|
|
120
|
+
utils.logLabeledBullet("extensions", `Configuring ${clc.bold(instanceSpec.instanceId)} extension instance`);
|
|
121
|
+
}
|
|
112
122
|
if (instanceSpec.ref) {
|
|
113
123
|
await extensionsApi.configureInstance({
|
|
114
124
|
projectId,
|
|
@@ -139,6 +149,7 @@ function configureExtensionInstanceTask(projectId, instanceSpec, validateOnly =
|
|
|
139
149
|
exports.configureExtensionInstanceTask = configureExtensionInstanceTask;
|
|
140
150
|
function deleteExtensionInstanceTask(projectId, instanceSpec) {
|
|
141
151
|
const run = async () => {
|
|
152
|
+
utils.logLabeledBullet("extensions", `Deleting ${clc.bold(instanceSpec.instanceId)} extension instance`);
|
|
142
153
|
await extensionsApi.deleteInstance(projectId, instanceSpec.instanceId);
|
|
143
154
|
printSuccess(instanceSpec.instanceId, "delete", false);
|
|
144
155
|
return;
|
|
@@ -5,6 +5,7 @@ const loadCJSON_1 = require("../../loadCJSON");
|
|
|
5
5
|
const rulesDeploy_1 = require("../../rulesDeploy");
|
|
6
6
|
const utils = require("../../utils");
|
|
7
7
|
const fsConfig = require("../../firestore/fsConfig");
|
|
8
|
+
const logger_1 = require("../../logger");
|
|
8
9
|
function prepareRules(context, rulesDeploy, databaseId, rulesFile) {
|
|
9
10
|
rulesDeploy.addFile(rulesFile);
|
|
10
11
|
context.firestore.rules.push({
|
|
@@ -23,6 +24,7 @@ function prepareIndexes(context, options, databaseId, indexesFileName) {
|
|
|
23
24
|
});
|
|
24
25
|
}
|
|
25
26
|
async function default_1(context, options) {
|
|
27
|
+
var _a;
|
|
26
28
|
if (options.only) {
|
|
27
29
|
const targets = options.only.split(",");
|
|
28
30
|
const excludeRules = targets.indexOf("firestore:indexes") >= 0;
|
|
@@ -57,5 +59,13 @@ async function default_1(context, options) {
|
|
|
57
59
|
if (context.firestore.rules.length > 0) {
|
|
58
60
|
await rulesDeploy.compile();
|
|
59
61
|
}
|
|
62
|
+
const rulesContext = (_a = context === null || context === void 0 ? void 0 : context.firestore) === null || _a === void 0 ? void 0 : _a.rules;
|
|
63
|
+
for (const ruleContext of rulesContext) {
|
|
64
|
+
const databaseId = ruleContext.databaseId;
|
|
65
|
+
const rulesFile = ruleContext.rulesFile;
|
|
66
|
+
if (!rulesFile) {
|
|
67
|
+
logger_1.logger.error(`Invalid firestore config for ${databaseId} database: ${JSON.stringify(options.config.src.firestore)}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
60
70
|
}
|
|
61
71
|
exports.default = default_1;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const rulesDeploy_1 = require("../../rulesDeploy");
|
|
4
|
-
|
|
5
|
-
async function default_1(context, options) {
|
|
4
|
+
async function default_1(context) {
|
|
6
5
|
var _a, _b;
|
|
7
6
|
const rulesDeploy = (_a = context === null || context === void 0 ? void 0 : context.firestore) === null || _a === void 0 ? void 0 : _a.rulesDeploy;
|
|
8
7
|
if (!context.firestoreRules || !rulesDeploy) {
|
|
@@ -12,11 +11,9 @@ async function default_1(context, options) {
|
|
|
12
11
|
await Promise.all(rulesContext.map(async (ruleContext) => {
|
|
13
12
|
const databaseId = ruleContext.databaseId;
|
|
14
13
|
const rulesFile = ruleContext.rulesFile;
|
|
15
|
-
if (
|
|
16
|
-
|
|
17
|
-
return;
|
|
14
|
+
if (rulesFile) {
|
|
15
|
+
return rulesDeploy.release(rulesFile, rulesDeploy_1.RulesetServiceType.CLOUD_FIRESTORE, databaseId);
|
|
18
16
|
}
|
|
19
|
-
return rulesDeploy.release(rulesFile, rulesDeploy_1.RulesetServiceType.CLOUD_FIRESTORE, databaseId);
|
|
20
17
|
}));
|
|
21
18
|
}
|
|
22
19
|
exports.default = default_1;
|
|
@@ -102,7 +102,7 @@ function obtainDefaultComputeServiceAgentBindings(projectNumber) {
|
|
|
102
102
|
return [runInvokerBinding, eventarcEventReceiverBinding];
|
|
103
103
|
}
|
|
104
104
|
exports.obtainDefaultComputeServiceAgentBindings = obtainDefaultComputeServiceAgentBindings;
|
|
105
|
-
async function ensureServiceAgentRoles(projectId, projectNumber, want, have) {
|
|
105
|
+
async function ensureServiceAgentRoles(projectId, projectNumber, want, have, dryRun) {
|
|
106
106
|
const wantServices = backend.allEndpoints(want).reduce(reduceEventsToServices, []);
|
|
107
107
|
const haveServices = backend.allEndpoints(have).reduce(reduceEventsToServices, []);
|
|
108
108
|
const newServices = wantServices.filter((wantS) => !haveServices.find((haveS) => wantS.name === haveS.name));
|
|
@@ -138,7 +138,12 @@ async function ensureServiceAgentRoles(projectId, projectNumber, want, have) {
|
|
|
138
138
|
return;
|
|
139
139
|
}
|
|
140
140
|
try {
|
|
141
|
-
|
|
141
|
+
if (dryRun) {
|
|
142
|
+
logger_1.logger.info(`On your next deploy, the following required roles will be granted: ${requiredBindings.map((b) => `${b.members.join(", ")}: ${(0, colorette_1.bold)(b.role)}`)}`);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
await (0, resourceManager_1.setIamPolicy)(projectNumber, policy, "bindings");
|
|
146
|
+
}
|
|
142
147
|
}
|
|
143
148
|
catch (err) {
|
|
144
149
|
iam.printManualIamConfig(requiredBindings, projectId, "functions");
|
|
@@ -79,11 +79,19 @@ async function secretsToServiceAccounts(b) {
|
|
|
79
79
|
}
|
|
80
80
|
return secretsToSa;
|
|
81
81
|
}
|
|
82
|
-
async function secretAccess(projectId, wantBackend, haveBackend) {
|
|
82
|
+
async function secretAccess(projectId, wantBackend, haveBackend, dryRun) {
|
|
83
83
|
var _a, _b;
|
|
84
84
|
const ensureAccess = async (secret, serviceAccounts) => {
|
|
85
85
|
(0, utils_1.logLabeledBullet)("functions", `ensuring ${clc.bold(serviceAccounts.join(", "))} access to secret ${clc.bold(secret)}.`);
|
|
86
|
-
|
|
86
|
+
if (dryRun) {
|
|
87
|
+
const check = await (0, secretManager_1.checkServiceAgentRole)({ name: secret, projectId }, serviceAccounts, "roles/secretmanager.secretAccessor");
|
|
88
|
+
if (check.length) {
|
|
89
|
+
(0, utils_1.logLabeledBullet)("functions", `On your next deploy, ${clc.bold(serviceAccounts.join(", "))} will be granted access to secret ${clc.bold(secret)}.`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
await (0, secretManager_1.ensureServiceAgentRole)({ name: secret, projectId }, serviceAccounts, "roles/secretmanager.secretAccessor");
|
|
94
|
+
}
|
|
87
95
|
(0, utils_1.logLabeledSuccess)("functions", `ensured ${clc.bold(serviceAccounts.join(", "))} access to ${clc.bold(secret)}.`);
|
|
88
96
|
};
|
|
89
97
|
const wantSecrets = await secretsToServiceAccounts(wantBackend);
|
|
@@ -27,6 +27,7 @@ const serviceusage_1 = require("../../gcp/serviceusage");
|
|
|
27
27
|
const applyHash_1 = require("./cache/applyHash");
|
|
28
28
|
const backend_1 = require("./backend");
|
|
29
29
|
const functional_1 = require("../../functional");
|
|
30
|
+
const prepare_1 = require("../extensions/prepare");
|
|
30
31
|
exports.EVENTARC_SOURCE_ENV = "EVENTARC_CLOUD_EVENT_SOURCE";
|
|
31
32
|
async function prepare(context, options, payload) {
|
|
32
33
|
var _a, _b;
|
|
@@ -55,6 +56,13 @@ async function prepare(context, options, payload) {
|
|
|
55
56
|
}
|
|
56
57
|
context.codebaseDeployEvents = {};
|
|
57
58
|
const wantBuilds = await loadCodebases(context.config, options, firebaseConfig, runtimeConfig, context.filters);
|
|
59
|
+
if (Object.values(wantBuilds).some((b) => b.extensions)) {
|
|
60
|
+
const extContext = {};
|
|
61
|
+
const extPayload = {};
|
|
62
|
+
await (0, prepare_1.prepareDynamicExtensions)(extContext, options, extPayload, wantBuilds);
|
|
63
|
+
context.extensions = extContext;
|
|
64
|
+
payload.extensions = extPayload;
|
|
65
|
+
}
|
|
58
66
|
const codebaseUsesEnvs = [];
|
|
59
67
|
const wantBackends = {};
|
|
60
68
|
for (const [codebase, wantBuild] of Object.entries(wantBuilds)) {
|
|
@@ -183,9 +191,9 @@ async function prepare(context, options, payload) {
|
|
|
183
191
|
await (0, prompts_1.promptForFailurePolicies)(options, matchingBackend, haveBackend);
|
|
184
192
|
await (0, prompts_1.promptForMinInstances)(options, matchingBackend, haveBackend);
|
|
185
193
|
await backend.checkAvailability(context, matchingBackend);
|
|
186
|
-
await (0, checkIam_1.ensureServiceAgentRoles)(projectId, projectNumber, matchingBackend, haveBackend);
|
|
187
194
|
await validate.secretsAreValid(projectId, matchingBackend);
|
|
188
|
-
await
|
|
195
|
+
await (0, checkIam_1.ensureServiceAgentRoles)(projectId, projectNumber, matchingBackend, haveBackend, options.dryRun);
|
|
196
|
+
await ensure.secretAccess(projectId, matchingBackend, haveBackend, options.dryRun);
|
|
189
197
|
updateEndpointTargetedStatus(wantBackends, context.filters || []);
|
|
190
198
|
(0, applyHash_1.applyBackendHashToBackends)(wantBackends, context);
|
|
191
199
|
}
|
|
@@ -18,7 +18,7 @@ const validate = require("./validate");
|
|
|
18
18
|
const versioning = require("./versioning");
|
|
19
19
|
const parseTriggers = require("./parseTriggers");
|
|
20
20
|
const fsutils_1 = require("../../../../fsutils");
|
|
21
|
-
const MIN_FUNCTIONS_SDK_VERSION = "
|
|
21
|
+
const MIN_FUNCTIONS_SDK_VERSION = "5.1.0";
|
|
22
22
|
async function tryCreateDelegate(context) {
|
|
23
23
|
const packageJsonPath = path.join(context.sourceDir, "package.json");
|
|
24
24
|
if (!(await (0, util_1.promisify)(fs.exists)(packageJsonPath))) {
|
package/lib/deploy/index.js
CHANGED
|
@@ -41,6 +41,7 @@ const chain = async function (fns, context, options, payload) {
|
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
43
|
const deploy = async function (targetNames, options, customContext = {}) {
|
|
44
|
+
var _a;
|
|
44
45
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
45
46
|
const payload = {};
|
|
46
47
|
const context = Object.assign({ projectId }, customContext);
|
|
@@ -85,9 +86,11 @@ const deploy = async function (targetNames, options, customContext = {}) {
|
|
|
85
86
|
}
|
|
86
87
|
predeploys.push((0, lifecycleHooks_1.lifecycleHooks)(targetName, "predeploy"));
|
|
87
88
|
prepares.push(target.prepare);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
if (!options.dryRun) {
|
|
90
|
+
deploys.push(target.deploy);
|
|
91
|
+
releases.push(target.release);
|
|
92
|
+
postdeploys.push((0, lifecycleHooks_1.lifecycleHooks)(targetName, "postdeploy"));
|
|
93
|
+
}
|
|
91
94
|
}
|
|
92
95
|
logger_1.logger.info();
|
|
93
96
|
logger_1.logger.info((0, colorette_1.bold)((0, colorette_1.white)("===") + " Deploying to '" + projectId + "'..."));
|
|
@@ -110,11 +113,12 @@ const deploy = async function (targetNames, options, customContext = {}) {
|
|
|
110
113
|
analyticsParams[t] = "true";
|
|
111
114
|
}
|
|
112
115
|
await (0, track_1.trackGA4)("product_deploy", analyticsParams, duration);
|
|
116
|
+
const successMessage = options.dryRun ? "Dry run complete!" : "Deploy complete!";
|
|
113
117
|
logger_1.logger.info();
|
|
114
|
-
(0, utils_1.logSuccess)((0, colorette_1.bold)((0, colorette_1.underline)(
|
|
118
|
+
(0, utils_1.logSuccess)((0, colorette_1.bold)((0, colorette_1.underline)(successMessage)));
|
|
115
119
|
logger_1.logger.info();
|
|
116
120
|
const deployedHosting = (0, lodash_1.includes)(targetNames, "hosting");
|
|
117
|
-
logger_1.logger.info((0, colorette_1.bold)("Project Console:"), (0, utils_1.consoleUrl)(options.project, "/overview"));
|
|
121
|
+
logger_1.logger.info((0, colorette_1.bold)("Project Console:"), (0, utils_1.consoleUrl)((_a = options.project) !== null && _a !== void 0 ? _a : "_", "/overview"));
|
|
118
122
|
if (deployedHosting) {
|
|
119
123
|
(0, lodash_1.each)(context.hosting.deploys, (deploy) => {
|
|
120
124
|
logger_1.logger.info((0, colorette_1.bold)("Hosting URL:"), (0, utils_1.addSubdomain)((0, api_1.hostingOrigin)(), deploy.config.site));
|
|
@@ -46,20 +46,20 @@ const EMULATOR_UPDATE_DETAILS = {
|
|
|
46
46
|
},
|
|
47
47
|
dataconnect: process.platform === "darwin"
|
|
48
48
|
? {
|
|
49
|
-
version: "1.3.
|
|
50
|
-
expectedSize:
|
|
51
|
-
expectedChecksum: "
|
|
49
|
+
version: "1.3.8",
|
|
50
|
+
expectedSize: 25027328,
|
|
51
|
+
expectedChecksum: "903f0e8dd8212fec575dc2eba34be6e7",
|
|
52
52
|
}
|
|
53
53
|
: process.platform === "win32"
|
|
54
54
|
? {
|
|
55
|
-
version: "1.3.
|
|
56
|
-
expectedSize:
|
|
57
|
-
expectedChecksum: "
|
|
55
|
+
version: "1.3.8",
|
|
56
|
+
expectedSize: 25450496,
|
|
57
|
+
expectedChecksum: "e09999deda7301eba06f88face5e1744",
|
|
58
58
|
}
|
|
59
59
|
: {
|
|
60
|
-
version: "1.3.
|
|
61
|
-
expectedSize:
|
|
62
|
-
expectedChecksum: "
|
|
60
|
+
version: "1.3.8",
|
|
61
|
+
expectedSize: 24940696,
|
|
62
|
+
expectedChecksum: "69aeb9755d4ec2e77bdadbc40236b306",
|
|
63
63
|
},
|
|
64
64
|
};
|
|
65
65
|
exports.DownloadDetails = {
|
|
@@ -15,10 +15,11 @@ const extensionsApiClient = new apiv2_1.Client({
|
|
|
15
15
|
urlPrefix: (0, api_1.extensionsOrigin)(),
|
|
16
16
|
apiVersion: EXTENSIONS_API_VERSION,
|
|
17
17
|
});
|
|
18
|
-
async function createInstanceHelper(projectId, instanceId, config, validateOnly = false) {
|
|
18
|
+
async function createInstanceHelper(projectId, instanceId, config, labels, validateOnly = false) {
|
|
19
19
|
const createRes = await extensionsApiClient.post(`/projects/${projectId}/instances/`, {
|
|
20
20
|
name: `projects/${projectId}/instances/${instanceId}`,
|
|
21
21
|
config,
|
|
22
|
+
labels,
|
|
22
23
|
}, {
|
|
23
24
|
queryParams: {
|
|
24
25
|
validateOnly: validateOnly ? "true" : "false",
|
|
@@ -63,7 +64,7 @@ async function createInstance(args) {
|
|
|
63
64
|
if (args.eventarcChannel) {
|
|
64
65
|
config.eventarcChannel = args.eventarcChannel;
|
|
65
66
|
}
|
|
66
|
-
return createInstanceHelper(args.projectId, args.instanceId, config, args.validateOnly);
|
|
67
|
+
return await createInstanceHelper(args.projectId, args.instanceId, config, args.labels, args.validateOnly);
|
|
67
68
|
}
|
|
68
69
|
exports.createInstance = createInstance;
|
|
69
70
|
async function deleteInstance(projectId, instanceId) {
|
|
@@ -7,7 +7,7 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
|
7
7
|
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
8
8
|
};
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.diagnoseAndFixProject = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.getMissingPublisherError = exports.uploadExtensionVersionFromLocalSource = exports.uploadExtensionVersionFromGitHubSource = exports.unpackExtensionState = exports.getNextVersionByStage = exports.ensureExtensionsPublisherApiEnabled = exports.ensureExtensionsApiEnabled = exports.checkExtensionsApiEnabled = exports.promptForExtensionRoot = exports.promptForValidRepoURI = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteSecretParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.
|
|
10
|
+
exports.diagnoseAndFixProject = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.getMissingPublisherError = exports.uploadExtensionVersionFromLocalSource = exports.uploadExtensionVersionFromGitHubSource = exports.unpackExtensionState = exports.getNextVersionByStage = exports.ensureExtensionsPublisherApiEnabled = exports.ensureExtensionsApiEnabled = exports.checkExtensionsApiEnabled = exports.promptForExtensionRoot = exports.promptForValidRepoURI = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteSecretParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOPULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
|
|
11
11
|
const clc = require("colorette");
|
|
12
12
|
const ora = require("ora");
|
|
13
13
|
const semver = require("semver");
|
|
@@ -69,7 +69,7 @@ const AUTOPOPULATED_PARAM_NAMES = [
|
|
|
69
69
|
"DATABASE_INSTANCE",
|
|
70
70
|
"DATABASE_URL",
|
|
71
71
|
];
|
|
72
|
-
exports.
|
|
72
|
+
exports.AUTOPOPULATED_PARAM_PLACEHOLDERS = {
|
|
73
73
|
PROJECT_ID: "project-id",
|
|
74
74
|
STORAGE_BUCKET: "project-id.appspot.com",
|
|
75
75
|
EXT_INSTANCE_ID: "extension-id",
|
|
@@ -449,7 +449,7 @@ async function validateExtensionSpec(rootDirectory, extensionId) {
|
|
|
449
449
|
throw new error_1.FirebaseError(`Extension ID '${clc.bold(extensionId)}' does not match the name in extension.yaml '${clc.bold(extensionSpec.name)}'.`);
|
|
450
450
|
}
|
|
451
451
|
const subbedSpec = JSON.parse(JSON.stringify(extensionSpec));
|
|
452
|
-
subbedSpec.params = substituteParams(extensionSpec.params || [], exports.
|
|
452
|
+
subbedSpec.params = substituteParams(extensionSpec.params || [], exports.AUTOPOPULATED_PARAM_PLACEHOLDERS);
|
|
453
453
|
validateSpec(subbedSpec);
|
|
454
454
|
return extensionSpec;
|
|
455
455
|
}
|
|
@@ -6,11 +6,20 @@ const path = require("path");
|
|
|
6
6
|
const yaml = require("yaml");
|
|
7
7
|
const fsutils_1 = require("../fsutils");
|
|
8
8
|
const error_1 = require("../error");
|
|
9
|
+
const types_1 = require("./types");
|
|
9
10
|
const logger_1 = require("../logger");
|
|
11
|
+
const extensionsHelper_1 = require("./extensionsHelper");
|
|
10
12
|
exports.EXTENSIONS_SPEC_FILE = "extension.yaml";
|
|
11
13
|
const EXTENSIONS_PREINSTALL_FILE = "PREINSTALL.md";
|
|
12
14
|
async function getLocalExtensionSpec(directory) {
|
|
13
15
|
const spec = await parseYAML(readFile(path.resolve(directory, exports.EXTENSIONS_SPEC_FILE)));
|
|
16
|
+
if (spec.lifecycleEvents) {
|
|
17
|
+
spec.lifecycleEvents = fixLifecycleEvents(spec.lifecycleEvents);
|
|
18
|
+
}
|
|
19
|
+
if (!(0, types_1.isExtensionSpec)(spec)) {
|
|
20
|
+
(0, extensionsHelper_1.validateSpec)(spec);
|
|
21
|
+
throw new error_1.FirebaseError("Error: extension.yaml does not contain a valid extension specification.");
|
|
22
|
+
}
|
|
14
23
|
try {
|
|
15
24
|
const preinstall = readFile(path.resolve(directory, EXTENSIONS_PREINSTALL_FILE));
|
|
16
25
|
spec.preinstallContent = preinstall;
|
|
@@ -21,6 +30,28 @@ async function getLocalExtensionSpec(directory) {
|
|
|
21
30
|
return spec;
|
|
22
31
|
}
|
|
23
32
|
exports.getLocalExtensionSpec = getLocalExtensionSpec;
|
|
33
|
+
function fixLifecycleEvents(lifecycleEvents) {
|
|
34
|
+
const stages = {
|
|
35
|
+
onInstall: "ON_INSTALL",
|
|
36
|
+
onUpdate: "ON_UPDATE",
|
|
37
|
+
onConfigure: "ON_CONFIGURE",
|
|
38
|
+
stageUnspecified: "STAGE_UNSPECIFIED",
|
|
39
|
+
};
|
|
40
|
+
const arrayLifecycle = [];
|
|
41
|
+
if ((0, types_1.isObject)(lifecycleEvents)) {
|
|
42
|
+
for (const [key, val] of Object.entries(lifecycleEvents)) {
|
|
43
|
+
if ((0, types_1.isObject)(val) &&
|
|
44
|
+
typeof val.function === "string" &&
|
|
45
|
+
typeof val.processingMessage === "string") {
|
|
46
|
+
arrayLifecycle.push({
|
|
47
|
+
stage: stages[key] || stages["stageUnspecified"],
|
|
48
|
+
taskQueueTriggerFunction: val.function,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return arrayLifecycle;
|
|
54
|
+
}
|
|
24
55
|
function findExtensionYaml(directory) {
|
|
25
56
|
while (!(0, fsutils_1.fileExistsSync)(path.resolve(directory, exports.EXTENSIONS_SPEC_FILE))) {
|
|
26
57
|
const parentDir = path.dirname(directory);
|