firebase-tools 13.15.0 → 13.15.2
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/commands/deploy.js +3 -1
- package/lib/dataconnect/fileUtils.js +52 -11
- package/lib/dataconnect/provisionCloudSql.js +5 -1
- package/lib/deploy/extensions/planner.js +46 -1
- package/lib/deploy/extensions/prepare.js +99 -23
- package/lib/deploy/functions/build.js +5 -5
- package/lib/deploy/functions/deploy.js +12 -12
- package/lib/deploy/functions/params.js +5 -3
- package/lib/deploy/functions/prepare.js +16 -1
- package/lib/deploy/functions/release/index.js +4 -0
- package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +46 -0
- package/lib/emulator/downloadableEmulators.js +9 -9
- package/lib/emulator/functionsEmulator.js +8 -1
- package/lib/extensions/askUserForEventsConfig.js +18 -8
- package/lib/extensions/askUserForParam.js +2 -1
- package/lib/extensions/displayExtensionInfo.js +5 -5
- package/lib/extensions/extensionsApi.js +1 -1
- package/lib/extensions/extensionsHelper.js +42 -3
- package/lib/extensions/localHelper.js +1 -1
- package/lib/extensions/refs.js +26 -11
- package/lib/extensions/runtimes/common.js +75 -0
- package/lib/extensions/types.js +56 -1
- package/lib/frameworks/constants.js +1 -1
- package/lib/frameworks/next/constants.js +1 -1
- package/lib/frameworks/next/index.js +35 -15
- package/lib/frameworks/next/utils.js +53 -1
- package/lib/init/features/dataconnect/index.js +10 -7
- package/lib/init/features/dataconnect/sdk.js +67 -51
- package/lib/prompt.js +22 -1
- package/package.json +1 -1
- package/templates/init/dataconnect/connector.yaml +5 -3
- package/templates/init/dataconnect/dataconnect.yaml +5 -5
- package/templates/init/dataconnect/mutations.gql +44 -29
- package/templates/init/dataconnect/queries.gql +66 -38
- package/templates/init/dataconnect/schema.gql +38 -21
package/lib/commands/deploy.js
CHANGED
|
@@ -80,7 +80,9 @@ exports.command = new command_1.Command("deploy")
|
|
|
80
80
|
.option("--only <targets>", 'only deploy to specified, comma-separated targets (e.g. "hosting,storage"). For functions, ' +
|
|
81
81
|
'can specify filters with colons to scope function deploys to only those functions (e.g. "--only functions:func1,functions:func2"). ' +
|
|
82
82
|
"When filtering based on export groups (the exported module object keys), use dots to specify group names " +
|
|
83
|
-
'(e.g. "--only functions:group1.subgroup1,functions:group2)
|
|
83
|
+
'(e.g. "--only functions:group1.subgroup1,functions:group2"). ' +
|
|
84
|
+
"When filtering based on codebases, use colons to specify codebase names " +
|
|
85
|
+
'(e.g. "--only functions:codebase1:func1,functions:codebase2:group1.subgroup1"). ' +
|
|
84
86
|
"For data connect, can specify filters with colons to deploy only a service, connector, or schema" +
|
|
85
87
|
'(e.g. "--only dataconnect:serviceId,dataconnect:serviceId:connectorId,dataconnect:serviceId:schema"). ')
|
|
86
88
|
.option("--except <targets>", 'deploy to all targets except specified (e.g. "database")')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getPlatformFromFolder = exports.pickService = exports.readGQLFiles = exports.readConnectorYaml = exports.readDataConnectYaml = exports.readFirebaseJson = void 0;
|
|
3
|
+
exports.generateSdkYaml = exports.directoryHasPackageJson = exports.getPlatformFromFolder = exports.pickService = exports.readGQLFiles = exports.readConnectorYaml = exports.readDataConnectYaml = exports.readFirebaseJson = void 0;
|
|
4
4
|
const fs = require("fs-extra");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const error_1 = require("../error");
|
|
@@ -95,21 +95,62 @@ async function pickService(projectId, config, serviceId) {
|
|
|
95
95
|
exports.pickService = pickService;
|
|
96
96
|
const WEB_INDICATORS = ["package.json", "package-lock.json", "node_modules"];
|
|
97
97
|
const IOS_INDICATORS = ["info.plist", "podfile", "package.swift"];
|
|
98
|
-
const ANDROID_INDICATORS = ["androidmanifest.xml", "build.gradle"];
|
|
99
|
-
const
|
|
98
|
+
const ANDROID_INDICATORS = ["androidmanifest.xml", "build.gradle", "build.gradle.kts"];
|
|
99
|
+
const IOS_POSTFIX_INDICATORS = [".xcworkspace", ".xcodeproj"];
|
|
100
100
|
async function getPlatformFromFolder(dirPath) {
|
|
101
101
|
const fileNames = await fs.readdir(dirPath);
|
|
102
|
+
let hasWeb = false;
|
|
103
|
+
let hasAndroid = false;
|
|
104
|
+
let hasIOS = false;
|
|
102
105
|
for (const fileName of fileNames) {
|
|
103
106
|
const cleanedFileName = fileName.toLowerCase();
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
hasWeb || (hasWeb = WEB_INDICATORS.some((indicator) => indicator === cleanedFileName));
|
|
108
|
+
hasAndroid || (hasAndroid = ANDROID_INDICATORS.some((indicator) => indicator === cleanedFileName));
|
|
109
|
+
hasIOS || (hasIOS = IOS_INDICATORS.some((indicator) => indicator === cleanedFileName) ||
|
|
110
|
+
IOS_POSTFIX_INDICATORS.some((indicator) => cleanedFileName.endsWith(indicator)));
|
|
111
|
+
}
|
|
112
|
+
if (hasWeb && !hasAndroid && !hasIOS) {
|
|
113
|
+
return types_1.Platform.WEB;
|
|
114
|
+
}
|
|
115
|
+
else if (hasAndroid && !hasWeb && !hasIOS) {
|
|
116
|
+
return types_1.Platform.ANDROID;
|
|
117
|
+
}
|
|
118
|
+
else if (hasIOS && !hasWeb && !hasAndroid) {
|
|
119
|
+
return types_1.Platform.IOS;
|
|
112
120
|
}
|
|
113
121
|
return types_1.Platform.UNDETERMINED;
|
|
114
122
|
}
|
|
115
123
|
exports.getPlatformFromFolder = getPlatformFromFolder;
|
|
124
|
+
async function directoryHasPackageJson(dirPath) {
|
|
125
|
+
const fileNames = await fs.readdir(dirPath);
|
|
126
|
+
return fileNames.some((f) => f.toLowerCase() === "package.json");
|
|
127
|
+
}
|
|
128
|
+
exports.directoryHasPackageJson = directoryHasPackageJson;
|
|
129
|
+
function generateSdkYaml(platform, connectorYaml, connectorYamlFolder, appFolder) {
|
|
130
|
+
const relPath = path.relative(connectorYamlFolder, appFolder);
|
|
131
|
+
const outputDir = path.join(relPath, "dataconnect-generated");
|
|
132
|
+
if (!connectorYaml.generate) {
|
|
133
|
+
connectorYaml.generate = {};
|
|
134
|
+
}
|
|
135
|
+
if (platform === types_1.Platform.WEB) {
|
|
136
|
+
connectorYaml.generate.javascriptSdk = {
|
|
137
|
+
outputDir,
|
|
138
|
+
package: `@firebasegen/${connectorYaml.connectorId}`,
|
|
139
|
+
packageJsonDir: appFolder,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
if (platform === types_1.Platform.IOS) {
|
|
143
|
+
connectorYaml.generate.swiftSdk = {
|
|
144
|
+
outputDir,
|
|
145
|
+
package: connectorYaml.connectorId,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
if (platform === types_1.Platform.ANDROID) {
|
|
149
|
+
connectorYaml.generate.kotlinSdk = {
|
|
150
|
+
outputDir,
|
|
151
|
+
package: `connectors.${connectorYaml.connectorId}`,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return connectorYaml;
|
|
155
|
+
}
|
|
156
|
+
exports.generateSdkYaml = generateSdkYaml;
|
|
@@ -5,6 +5,7 @@ const cloudSqlAdminClient = require("../gcp/cloudsql/cloudsqladmin");
|
|
|
5
5
|
const utils = require("../utils");
|
|
6
6
|
const checkIam_1 = require("./checkIam");
|
|
7
7
|
const utils_1 = require("../utils");
|
|
8
|
+
const logger_1 = require("../logger");
|
|
8
9
|
const GOOGLE_ML_INTEGRATION_ROLE = "roles/aiplatform.user";
|
|
9
10
|
const freeTrial_1 = require("./freeTrial");
|
|
10
11
|
const error_1 = require("../error");
|
|
@@ -44,7 +45,9 @@ async function provisionCloudSql(args) {
|
|
|
44
45
|
connectionName = (newInstance === null || newInstance === void 0 ? void 0 : newInstance.connectionName) || "";
|
|
45
46
|
}
|
|
46
47
|
else {
|
|
47
|
-
silent ||
|
|
48
|
+
silent ||
|
|
49
|
+
utils.logLabeledBullet("dataconnect", "Cloud SQL instance creation started - it should be ready shortly. Database and users will be created on your next deploy.");
|
|
50
|
+
return connectionName;
|
|
48
51
|
}
|
|
49
52
|
}
|
|
50
53
|
try {
|
|
@@ -59,6 +62,7 @@ async function provisionCloudSql(args) {
|
|
|
59
62
|
silent || utils.logLabeledBullet("dataconnect", `Database ${databaseId} created.`);
|
|
60
63
|
}
|
|
61
64
|
else {
|
|
65
|
+
logger_1.logger.debug(`Unexpected error from CloudSQL: ${err}`);
|
|
62
66
|
silent || utils.logLabeledWarning("dataconnect", `Database ${databaseId} is not accessible.`);
|
|
63
67
|
}
|
|
64
68
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveVersion = exports.want = exports.have = exports.getExtensionSpec = exports.getExtension = exports.getExtensionVersion = void 0;
|
|
3
|
+
exports.resolveVersion = exports.want = exports.wantDynamic = exports.have = exports.getExtensionSpec = exports.getExtension = exports.getExtensionVersion = void 0;
|
|
4
4
|
const semver = require("semver");
|
|
5
5
|
const extensionsApi = require("../../extensions/extensionsApi");
|
|
6
6
|
const refs = require("../../extensions/refs");
|
|
@@ -11,6 +11,7 @@ const manifest_1 = require("../../extensions/manifest");
|
|
|
11
11
|
const paramHelper_1 = require("../../extensions/paramHelper");
|
|
12
12
|
const specHelper_1 = require("../../extensions/emulator/specHelper");
|
|
13
13
|
const functional_1 = require("../../functional");
|
|
14
|
+
const askUserForEventsConfig_1 = require("../../extensions/askUserForEventsConfig");
|
|
14
15
|
async function getExtensionVersion(i) {
|
|
15
16
|
if (!i.extensionVersion) {
|
|
16
17
|
if (!i.ref) {
|
|
@@ -69,6 +70,50 @@ async function have(projectId) {
|
|
|
69
70
|
});
|
|
70
71
|
}
|
|
71
72
|
exports.have = have;
|
|
73
|
+
async function wantDynamic(args) {
|
|
74
|
+
const instanceSpecs = [];
|
|
75
|
+
const errors = [];
|
|
76
|
+
for (const [instanceId, ext] of Object.entries(args.extensions)) {
|
|
77
|
+
const autoPopulatedParams = await (0, extensionsHelper_1.getFirebaseProjectParams)(args.projectId, args.emulatorMode);
|
|
78
|
+
const subbedParams = (0, extensionsHelper_1.substituteParams)(ext.params, autoPopulatedParams);
|
|
79
|
+
const eventarcChannel = ext.params["_EVENT_ARC_REGION"]
|
|
80
|
+
? (0, askUserForEventsConfig_1.getEventArcChannel)(args.projectId, ext.params["_EVENT_ARC_REGION"])
|
|
81
|
+
: undefined;
|
|
82
|
+
delete subbedParams["_EVENT_ARC_REGION"];
|
|
83
|
+
const subbedSecretParams = await (0, extensionsHelper_1.substituteSecretParams)(args.projectNumber, subbedParams);
|
|
84
|
+
const [systemParams, params] = (0, functional_1.partitionRecord)(subbedSecretParams, paramHelper_1.isSystemParam);
|
|
85
|
+
const allowedEventTypes = ext.events.length ? ext.events : undefined;
|
|
86
|
+
if (allowedEventTypes && !eventarcChannel) {
|
|
87
|
+
errors.push(new error_1.FirebaseError("EventArcRegion must be specified if event handlers are defined"));
|
|
88
|
+
}
|
|
89
|
+
if (ext.localPath) {
|
|
90
|
+
instanceSpecs.push({
|
|
91
|
+
instanceId,
|
|
92
|
+
localPath: ext.localPath,
|
|
93
|
+
params,
|
|
94
|
+
systemParams,
|
|
95
|
+
allowedEventTypes,
|
|
96
|
+
eventarcChannel,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
else if (ext.ref) {
|
|
100
|
+
instanceSpecs.push({
|
|
101
|
+
instanceId,
|
|
102
|
+
ref: refs.parse(ext.ref),
|
|
103
|
+
params,
|
|
104
|
+
systemParams,
|
|
105
|
+
allowedEventTypes,
|
|
106
|
+
eventarcChannel,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (errors.length) {
|
|
111
|
+
const messages = errors.map((err) => `- ${err.message}`).join("\n");
|
|
112
|
+
throw new error_1.FirebaseError(`Errors while reading 'extensions' in app code\n${messages}`);
|
|
113
|
+
}
|
|
114
|
+
return instanceSpecs;
|
|
115
|
+
}
|
|
116
|
+
exports.wantDynamic = wantDynamic;
|
|
72
117
|
async function want(args) {
|
|
73
118
|
const instanceSpecs = [];
|
|
74
119
|
const errors = [];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.prepare = void 0;
|
|
3
|
+
exports.prepare = exports.prepareDynamicExtensions = void 0;
|
|
4
4
|
const planner = require("./planner");
|
|
5
5
|
const deploymentSummary = require("./deploymentSummary");
|
|
6
6
|
const prompt = require("../../prompt");
|
|
@@ -16,32 +16,29 @@ const warnings_1 = require("../../extensions/warnings");
|
|
|
16
16
|
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
|
+
const functionsDeployHelper_1 = require("../functions/functionsDeployHelper");
|
|
22
|
+
async function prepareHelper(context, options, payload, wantExtensions, noDeleteExtensions, isPrimaryCall) {
|
|
20
23
|
var _a, _b;
|
|
21
|
-
context.extensionsStartTime = Date.now();
|
|
22
24
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
23
|
-
const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
|
|
24
|
-
const aliases = (0, projectUtils_1.getAliases)(options, projectId);
|
|
25
|
-
await (0, extensionsHelper_1.ensureExtensionsApiEnabled)(options);
|
|
26
|
-
await (0, requirePermissions_1.requirePermissions)(options, ["firebaseextensions.instances.list"]);
|
|
27
25
|
context.have = await planner.have(projectId);
|
|
28
|
-
context.want =
|
|
29
|
-
projectId,
|
|
30
|
-
projectNumber,
|
|
31
|
-
aliases,
|
|
32
|
-
projectDir: options.config.projectDir,
|
|
33
|
-
extensions: options.config.get("extensions"),
|
|
34
|
-
});
|
|
26
|
+
context.want = wantExtensions;
|
|
35
27
|
const etagsChanged = (0, etags_1.detectEtagChanges)(options.rc, projectId, context.have);
|
|
36
28
|
if (etagsChanged.length) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
29
|
+
const wantChangedIds = wantExtensions
|
|
30
|
+
.map((e) => e.instanceId)
|
|
31
|
+
.filter((id) => etagsChanged.includes(id));
|
|
32
|
+
if (wantChangedIds.length) {
|
|
33
|
+
(0, warnings_1.outOfBandChangesWarning)(wantChangedIds);
|
|
34
|
+
if (!(await prompt.confirm({
|
|
35
|
+
message: `Do you wish to continue deploying these extension instances?`,
|
|
36
|
+
default: false,
|
|
37
|
+
nonInteractive: options.nonInteractive,
|
|
38
|
+
force: options.force,
|
|
39
|
+
}))) {
|
|
40
|
+
throw new error_1.FirebaseError("Deployment cancelled");
|
|
41
|
+
}
|
|
45
42
|
}
|
|
46
43
|
}
|
|
47
44
|
const usingSecrets = await Promise.all((_a = context.want) === null || _a === void 0 ? void 0 : _a.map(secrets_1.checkSpecForSecrets));
|
|
@@ -55,7 +52,7 @@ async function prepare(context, options, payload) {
|
|
|
55
52
|
payload.instancesToCreate = context.want.filter((i) => { var _a; return !((_a = context.have) === null || _a === void 0 ? void 0 : _a.some(matchesInstanceId(i))); });
|
|
56
53
|
payload.instancesToConfigure = context.want.filter((i) => { var _a; return (_a = context.have) === null || _a === void 0 ? void 0 : _a.some(isConfigure(i)); });
|
|
57
54
|
payload.instancesToUpdate = context.want.filter((i) => { var _a; return (_a = context.have) === null || _a === void 0 ? void 0 : _a.some(isUpdate(i)); });
|
|
58
|
-
payload.instancesToDelete = context.have.filter((i) => { var _a; return !((_a = context.want) === null || _a === void 0 ? void 0 : _a.some(matchesInstanceId(i))); });
|
|
55
|
+
payload.instancesToDelete = context.have.filter((i) => { var _a; return !((_a = context.want) === null || _a === void 0 ? void 0 : _a.some(matchesInstanceId(i))) && !(noDeleteExtensions === null || noDeleteExtensions === void 0 ? void 0 : noDeleteExtensions.some(matchesInstanceId(i))); });
|
|
59
56
|
if (await (0, warnings_1.displayWarningsForDeploy)(payload.instancesToCreate)) {
|
|
60
57
|
if (!(await prompt.confirm({
|
|
61
58
|
message: `Do you wish to continue deploying these extension instances?`,
|
|
@@ -67,6 +64,9 @@ async function prepare(context, options, payload) {
|
|
|
67
64
|
}
|
|
68
65
|
}
|
|
69
66
|
const permissionsNeeded = [];
|
|
67
|
+
if (!isPrimaryCall) {
|
|
68
|
+
payload.instancesToDelete = [];
|
|
69
|
+
}
|
|
70
70
|
if (payload.instancesToCreate.length) {
|
|
71
71
|
permissionsNeeded.push("firebaseextensions.instances.create");
|
|
72
72
|
logger_1.logger.info(deploymentSummary.createsSummary(payload.instancesToCreate));
|
|
@@ -98,6 +98,82 @@ async function prepare(context, options, payload) {
|
|
|
98
98
|
await (0, requirePermissions_1.requirePermissions)(options, permissionsNeeded);
|
|
99
99
|
await (0, tos_1.acceptLatestAppDeveloperTOS)(options, projectId, context.want.map((i) => i.instanceId));
|
|
100
100
|
}
|
|
101
|
+
async function prepareDynamicExtensions(context, options, payload, builds) {
|
|
102
|
+
const filters = (0, functionsDeployHelper_1.getEndpointFilters)(options);
|
|
103
|
+
const extensions = (0, common_1.extractExtensionsFromBuilds)(builds, filters);
|
|
104
|
+
if (Object.keys(extensions).length === 0) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
108
|
+
const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
|
|
109
|
+
const aliases = (0, projectUtils_1.getAliases)(options, projectId);
|
|
110
|
+
const projectDir = options.config.projectDir;
|
|
111
|
+
const isPrimaryCall = !!options.only && !options.only.split(",").includes("extensions");
|
|
112
|
+
if (isPrimaryCall) {
|
|
113
|
+
await (0, extensionsHelper_1.ensureExtensionsApiEnabled)(options);
|
|
114
|
+
}
|
|
115
|
+
await (0, requirePermissions_1.requirePermissions)(options, ["firebaseextensions.instances.list"]);
|
|
116
|
+
const dynamicWant = await planner.wantDynamic({
|
|
117
|
+
projectId,
|
|
118
|
+
projectNumber,
|
|
119
|
+
extensions,
|
|
120
|
+
});
|
|
121
|
+
let noDeleteExtensions = [];
|
|
122
|
+
if (isPrimaryCall) {
|
|
123
|
+
const firebaseJsonWant = await planner.want({
|
|
124
|
+
projectId,
|
|
125
|
+
projectNumber,
|
|
126
|
+
aliases,
|
|
127
|
+
projectDir,
|
|
128
|
+
extensions: options.config.get("extensions"),
|
|
129
|
+
});
|
|
130
|
+
noDeleteExtensions = noDeleteExtensions.concat(firebaseJsonWant);
|
|
131
|
+
if (hasNonDeployingCodebases(options)) {
|
|
132
|
+
const dynamicAll = await planner.wantDynamic({
|
|
133
|
+
projectId,
|
|
134
|
+
projectNumber,
|
|
135
|
+
extensions: await (0, common_1.extractAllDynamicExtensions)(options),
|
|
136
|
+
});
|
|
137
|
+
noDeleteExtensions = noDeleteExtensions.concat(dynamicAll);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return prepareHelper(context, options, payload, dynamicWant, noDeleteExtensions, isPrimaryCall);
|
|
141
|
+
}
|
|
142
|
+
exports.prepareDynamicExtensions = prepareDynamicExtensions;
|
|
143
|
+
function hasNonDeployingCodebases(options) {
|
|
144
|
+
const functionFilters = (0, functionsDeployHelper_1.getEndpointFilters)(options);
|
|
145
|
+
if (functionFilters === null || functionFilters === void 0 ? void 0 : functionFilters.length) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
const functionsConfig = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions);
|
|
149
|
+
const allCodebases = (0, functionsDeployHelper_1.targetCodebases)(functionsConfig);
|
|
150
|
+
const deployingCodebases = (0, functionsDeployHelper_1.targetCodebases)(functionsConfig, functionFilters);
|
|
151
|
+
if (allCodebases.length > deployingCodebases.length) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
async function prepare(context, options, payload) {
|
|
156
|
+
context.extensionsStartTime = Date.now();
|
|
157
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
158
|
+
const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
|
|
159
|
+
const aliases = (0, projectUtils_1.getAliases)(options, projectId);
|
|
160
|
+
const projectDir = options.config.projectDir;
|
|
161
|
+
await (0, extensionsHelper_1.ensureExtensionsApiEnabled)(options);
|
|
162
|
+
await (0, requirePermissions_1.requirePermissions)(options, ["firebaseextensions.instances.list"]);
|
|
163
|
+
const firebaseJsonWant = await planner.want({
|
|
164
|
+
projectId,
|
|
165
|
+
projectNumber,
|
|
166
|
+
aliases,
|
|
167
|
+
projectDir,
|
|
168
|
+
extensions: options.config.get("extensions"),
|
|
169
|
+
});
|
|
170
|
+
const dynamicWant = await planner.wantDynamic({
|
|
171
|
+
projectId,
|
|
172
|
+
projectNumber,
|
|
173
|
+
extensions: await (0, common_1.extractAllDynamicExtensions)(options),
|
|
174
|
+
});
|
|
175
|
+
return prepareHelper(context, options, payload, firebaseJsonWant, dynamicWant, true);
|
|
176
|
+
}
|
|
101
177
|
exports.prepare = prepare;
|
|
102
178
|
const matchesInstanceId = (dep) => (test) => {
|
|
103
179
|
return dep.instanceId === test.instanceId;
|
|
@@ -55,19 +55,19 @@ exports.AllIngressSettings = [
|
|
|
55
55
|
"ALLOW_INTERNAL_ONLY",
|
|
56
56
|
"ALLOW_INTERNAL_AND_GCLB",
|
|
57
57
|
];
|
|
58
|
-
async function resolveBackend(
|
|
58
|
+
async function resolveBackend(opts) {
|
|
59
59
|
let paramValues = {};
|
|
60
|
-
paramValues = await params.resolveParams(build.params, firebaseConfig, envWithTypes(build.params, userEnvs), nonInteractive);
|
|
60
|
+
paramValues = await params.resolveParams(opts.build.params, opts.firebaseConfig, envWithTypes(opts.build.params, opts.userEnvs), opts.nonInteractive, opts.isEmulator);
|
|
61
61
|
const toWrite = {};
|
|
62
62
|
for (const paramName of Object.keys(paramValues)) {
|
|
63
63
|
const paramValue = paramValues[paramName];
|
|
64
|
-
if (Object.prototype.hasOwnProperty.call(userEnvs, paramName) || paramValue.internal) {
|
|
64
|
+
if (Object.prototype.hasOwnProperty.call(opts.userEnvs, paramName) || paramValue.internal) {
|
|
65
65
|
continue;
|
|
66
66
|
}
|
|
67
67
|
toWrite[paramName] = paramValue.toString();
|
|
68
68
|
}
|
|
69
|
-
(0, env_1.writeUserEnvs)(toWrite, userEnvOpt);
|
|
70
|
-
return { backend: toBackend(build, paramValues), envs: paramValues };
|
|
69
|
+
(0, env_1.writeUserEnvs)(toWrite, opts.userEnvOpt);
|
|
70
|
+
return { backend: toBackend(opts.build, paramValues), envs: paramValues };
|
|
71
71
|
}
|
|
72
72
|
exports.resolveBackend = resolveBackend;
|
|
73
73
|
function envWithTypes(definedParams, rawEnvs) {
|
|
@@ -12,6 +12,7 @@ const gcf = require("../../gcp/cloudfunctions");
|
|
|
12
12
|
const gcfv2 = require("../../gcp/cloudfunctionsv2");
|
|
13
13
|
const backend = require("./backend");
|
|
14
14
|
const backend_1 = require("./backend");
|
|
15
|
+
const extensions_1 = require("../extensions");
|
|
15
16
|
(0, tmp_1.setGracefulCleanup)();
|
|
16
17
|
async function uploadSourceV1(projectId, source, wantBackend) {
|
|
17
18
|
const v1Endpoints = backend.allEndpoints(wantBackend).filter((e) => e.platform === "gcfv1");
|
|
@@ -77,21 +78,20 @@ async function uploadCodebase(context, codebase, wantBackend) {
|
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
80
|
async function deploy(context, options, payload) {
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
if (!payload.functions) {
|
|
84
|
-
return;
|
|
81
|
+
if (payload.extensions && context.extensions) {
|
|
82
|
+
await (0, extensions_1.deploy)(context.extensions, options, payload.extensions);
|
|
85
83
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
if (payload.functions && context.config) {
|
|
85
|
+
await (0, checkIam_1.checkHttpIam)(context, options, payload);
|
|
86
|
+
const uploads = [];
|
|
87
|
+
for (const [codebase, { wantBackend, haveBackend }] of Object.entries(payload.functions)) {
|
|
88
|
+
if (shouldUploadBeSkipped(context, wantBackend, haveBackend)) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
uploads.push(uploadCodebase(context, codebase, wantBackend));
|
|
91
92
|
}
|
|
92
|
-
|
|
93
|
+
await Promise.all(uploads);
|
|
93
94
|
}
|
|
94
|
-
await Promise.all(uploads);
|
|
95
95
|
}
|
|
96
96
|
exports.deploy = deploy;
|
|
97
97
|
function shouldUploadBeSkipped(context, wantBackend, haveBackend) {
|
|
@@ -154,7 +154,7 @@ function canSatisfyParam(param, value) {
|
|
|
154
154
|
}
|
|
155
155
|
(0, functional_1.assertExhaustive)(param);
|
|
156
156
|
}
|
|
157
|
-
async function resolveParams(params, firebaseConfig, userEnvs, nonInteractive) {
|
|
157
|
+
async function resolveParams(params, firebaseConfig, userEnvs, nonInteractive, isEmulator = false) {
|
|
158
158
|
const paramValues = populateDefaultParams(firebaseConfig);
|
|
159
159
|
const [resolved, outstanding] = (0, functional_1.partition)(params, (param) => {
|
|
160
160
|
return {}.hasOwnProperty.call(userEnvs, param.name);
|
|
@@ -163,8 +163,10 @@ async function resolveParams(params, firebaseConfig, userEnvs, nonInteractive) {
|
|
|
163
163
|
paramValues[param.name] = userEnvs[param.name];
|
|
164
164
|
}
|
|
165
165
|
const [needSecret, needPrompt] = (0, functional_1.partition)(outstanding, (param) => param.type === "secret");
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
if (!isEmulator) {
|
|
167
|
+
for (const param of needSecret) {
|
|
168
|
+
await handleSecret(param, firebaseConfig.projectId);
|
|
169
|
+
}
|
|
168
170
|
}
|
|
169
171
|
if (nonInteractive && needPrompt.length > 0) {
|
|
170
172
|
const envNames = outstanding.map((p) => p.name).join(", ");
|
|
@@ -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)) {
|
|
@@ -67,7 +75,14 @@ async function prepare(context, options, payload) {
|
|
|
67
75
|
};
|
|
68
76
|
const userEnvs = functionsEnv.loadUserEnvs(userEnvOpt);
|
|
69
77
|
const envs = Object.assign(Object.assign({}, userEnvs), firebaseEnvs);
|
|
70
|
-
const { backend: wantBackend, envs: resolvedEnvs } = await build.resolveBackend(
|
|
78
|
+
const { backend: wantBackend, envs: resolvedEnvs } = await build.resolveBackend({
|
|
79
|
+
build: wantBuild,
|
|
80
|
+
firebaseConfig,
|
|
81
|
+
userEnvOpt,
|
|
82
|
+
userEnvs,
|
|
83
|
+
nonInteractive: options.nonInteractive,
|
|
84
|
+
isEmulator: false,
|
|
85
|
+
});
|
|
71
86
|
let hasEnvsFromParams = false;
|
|
72
87
|
wantBackend.environmentVariables = envs;
|
|
73
88
|
for (const envName of Object.keys(resolvedEnvs)) {
|
|
@@ -16,7 +16,11 @@ const functionsConfig_1 = require("../../../functionsConfig");
|
|
|
16
16
|
const functionsDeployHelper_1 = require("../functionsDeployHelper");
|
|
17
17
|
const error_1 = require("../../../error");
|
|
18
18
|
const getProjectNumber_1 = require("../../../getProjectNumber");
|
|
19
|
+
const extensions_1 = require("../../extensions");
|
|
19
20
|
async function release(context, options, payload) {
|
|
21
|
+
if (context.extensions && payload.extensions) {
|
|
22
|
+
await (0, extensions_1.release)(context.extensions, options, payload.extensions);
|
|
23
|
+
}
|
|
20
24
|
if (!context.config) {
|
|
21
25
|
return;
|
|
22
26
|
}
|
|
@@ -21,7 +21,7 @@ function assertKeyTypes(prefix, yaml, schema) {
|
|
|
21
21
|
const key = keyAsString;
|
|
22
22
|
const fullKey = prefix ? `${prefix}.${keyAsString}` : keyAsString;
|
|
23
23
|
if (!schema[key] || schema[key] === "omit") {
|
|
24
|
-
throw new error_1.FirebaseError(`Unexpected key ${fullKey}. You may need to install a newer version of the Firebase CLI.`);
|
|
24
|
+
throw new error_1.FirebaseError(`Unexpected key '${fullKey}'. You may need to install a newer version of the Firebase CLI.`);
|
|
25
25
|
}
|
|
26
26
|
let schemaType = schema[key];
|
|
27
27
|
if (typeof schemaType === "function") {
|
|
@@ -21,6 +21,7 @@ function buildFromV1Alpha1(yaml, project, region, runtime) {
|
|
|
21
21
|
params: "array",
|
|
22
22
|
requiredAPIs: "array",
|
|
23
23
|
endpoints: "object",
|
|
24
|
+
extensions: "object",
|
|
24
25
|
});
|
|
25
26
|
const bd = build.empty();
|
|
26
27
|
bd.params = manifest.params || [];
|
|
@@ -31,6 +32,15 @@ function buildFromV1Alpha1(yaml, project, region, runtime) {
|
|
|
31
32
|
const be = parseEndpointForBuild(id, me, project, region, runtime);
|
|
32
33
|
bd.endpoints[id] = be;
|
|
33
34
|
}
|
|
35
|
+
if (manifest.extensions) {
|
|
36
|
+
bd.extensions = {};
|
|
37
|
+
for (const id of Object.keys(manifest.extensions)) {
|
|
38
|
+
const me = manifest.extensions[id];
|
|
39
|
+
assertBuildExtension(me, id);
|
|
40
|
+
const be = parseExtensionForBuild(me);
|
|
41
|
+
bd.extensions[id] = be;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
34
44
|
return bd;
|
|
35
45
|
}
|
|
36
46
|
exports.buildFromV1Alpha1 = buildFromV1Alpha1;
|
|
@@ -272,6 +282,42 @@ function parseEndpointForBuild(id, ep, project, defaultRegion, runtime) {
|
|
|
272
282
|
});
|
|
273
283
|
return parsed;
|
|
274
284
|
}
|
|
285
|
+
function assertBuildExtension(ex, id) {
|
|
286
|
+
const prefix = `extensions[${id}]`;
|
|
287
|
+
(0, parsing_1.assertKeyTypes)(prefix, ex, {
|
|
288
|
+
params: "object",
|
|
289
|
+
ref: "string?",
|
|
290
|
+
localPath: "string?",
|
|
291
|
+
events: "array",
|
|
292
|
+
});
|
|
293
|
+
let refOrPath = 0;
|
|
294
|
+
if (ex.ref) {
|
|
295
|
+
refOrPath++;
|
|
296
|
+
}
|
|
297
|
+
if (ex.localPath) {
|
|
298
|
+
refOrPath++;
|
|
299
|
+
}
|
|
300
|
+
if (refOrPath === 0) {
|
|
301
|
+
throw new error_1.FirebaseError(`Expected either extension reference or local path in extension: ${id}`);
|
|
302
|
+
}
|
|
303
|
+
if (refOrPath > 1) {
|
|
304
|
+
throw new error_1.FirebaseError(`Multiple definitions for extension ${id}. Do not specify both reference and local path.`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
function parseExtensionForBuild(ex) {
|
|
308
|
+
const parsed = {
|
|
309
|
+
params: {},
|
|
310
|
+
events: [],
|
|
311
|
+
};
|
|
312
|
+
if (ex.localPath) {
|
|
313
|
+
parsed.localPath = ex.localPath;
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
parsed.ref = ex.ref;
|
|
317
|
+
}
|
|
318
|
+
(0, proto_1.copyIfPresent)(parsed, ex, "params", "events");
|
|
319
|
+
return parsed;
|
|
320
|
+
}
|
|
275
321
|
function resolveChannelName(projectId, channel, defaultRegion) {
|
|
276
322
|
if (!channel.includes("/")) {
|
|
277
323
|
const location = defaultRegion;
|
|
@@ -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.5",
|
|
50
|
+
expectedSize: 24232704,
|
|
51
|
+
expectedChecksum: "4eabf613a4a5feeaa173e1375b62bde0",
|
|
52
52
|
}
|
|
53
53
|
: process.platform === "win32"
|
|
54
54
|
? {
|
|
55
|
-
version: "1.3.
|
|
56
|
-
expectedSize:
|
|
57
|
-
expectedChecksum: "
|
|
55
|
+
version: "1.3.5",
|
|
56
|
+
expectedSize: 24651264,
|
|
57
|
+
expectedChecksum: "c7b2b7168ff7226f4e5626ae7d13e0ca",
|
|
58
58
|
}
|
|
59
59
|
: {
|
|
60
|
-
version: "1.3.
|
|
61
|
-
expectedSize:
|
|
62
|
-
expectedChecksum: "
|
|
60
|
+
version: "1.3.5",
|
|
61
|
+
expectedSize: 24146072,
|
|
62
|
+
expectedChecksum: "1457937751ce25fa332cdc16b561d64b",
|
|
63
63
|
},
|
|
64
64
|
};
|
|
65
65
|
exports.DownloadDetails = {
|
|
@@ -315,7 +315,14 @@ class FunctionsEmulator {
|
|
|
315
315
|
};
|
|
316
316
|
const userEnvs = functionsEnv.loadUserEnvs(userEnvOpt);
|
|
317
317
|
const discoveredBuild = await runtimeDelegate.discoverBuild(runtimeConfig, environment);
|
|
318
|
-
const resolution = await (0, build_1.resolveBackend)(
|
|
318
|
+
const resolution = await (0, build_1.resolveBackend)({
|
|
319
|
+
build: discoveredBuild,
|
|
320
|
+
firebaseConfig: JSON.parse(firebaseConfig),
|
|
321
|
+
userEnvOpt,
|
|
322
|
+
userEnvs,
|
|
323
|
+
nonInteractive: false,
|
|
324
|
+
isEmulator: true,
|
|
325
|
+
});
|
|
319
326
|
const discoveredBackend = resolution.backend;
|
|
320
327
|
const endpoints = backend.allEndpoints(discoveredBackend);
|
|
321
328
|
(0, functionsEmulatorShared_1.prepareEndpoints)(endpoints);
|