firebase-tools 13.18.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 CHANGED
@@ -117,15 +117,16 @@ Detailed doc is [here](https://firebase.google.com/docs/cli/auth).
117
117
 
118
118
  ### Extensions Commands
119
119
 
120
- | Command | Description |
121
- | ----------------- | ------------------------------------------------------------------------------------------- |
122
- | **ext** | Display information on how to use ext commands and extensions installed to your project. |
123
- | **ext:configure** | Configure an existing extension instance. |
124
- | **ext:info** | Display information about an extension by name (extensionName@x.y.z for a specific version) |
125
- | **ext:install** | Install an extension. |
126
- | **ext:list** | List all the extensions that are installed in your Firebase project. |
127
- | **ext:uninstall** | Uninstall an extension that is installed in your Firebase project by Instance ID. |
128
- | **ext:update** | Update an existing extension instance to the latest version. |
120
+ | Command | Description |
121
+ | ------------------- | ------------------------------------------------------------------------------------------- |
122
+ | **ext** | Display information on how to use ext commands and extensions installed to your project. |
123
+ | **ext:configure** | Configure an existing extension instance. |
124
+ | **ext:info** | Display information about an extension by name (extensionName@x.y.z for a specific version) |
125
+ | **ext:install** | Install an extension. |
126
+ | **ext:sdk:install** | Install and SDK for an extension so you can define the extension in a functions codebase. |
127
+ | **ext:list** | List all the extensions that are installed in your Firebase project. |
128
+ | **ext:uninstall** | Uninstall an extension that is installed in your Firebase project by Instance ID. |
129
+ | **ext:update** | Update an existing extension instance to the latest version. |
129
130
 
130
131
  ### Cloud Firestore Commands
131
132
 
@@ -110,7 +110,9 @@ exports.command = new command_1.Command("ext:info <extensionName>")
110
110
  else {
111
111
  marked_1.marked.use((0, marked_terminal_1.markedTerminal)());
112
112
  logger_1.logger.info(await (0, marked_1.marked)(lines.join("\n")));
113
- utils.logLabeledBullet(extensionsHelper_1.logPrefix, `to install this extension, type ` +
113
+ utils.logLabeledBullet(extensionsHelper_1.logPrefix, `to install this extension, run ` +
114
114
  clc.bold(`firebase ext:install ${extensionName} --project=YOUR_PROJECT`));
115
+ utils.logLabeledBullet(extensionsHelper_1.logPrefix, `to install an autogenerated SDK for this extension into your functions codebase, run ` +
116
+ clc.bold(`firebase ext:sdk:install ${extensionName} --project=YOUR_PROJECT`));
115
117
  }
116
118
  });
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const clc = require("colorette");
5
+ const semver = require("semver");
6
+ const checkMinRequiredVersion_1 = require("../checkMinRequiredVersion");
7
+ const command_1 = require("../command");
8
+ const extensionsApi = require("../extensions/extensionsApi");
9
+ const extensionsHelper_1 = require("../extensions/extensionsHelper");
10
+ const localHelper_1 = require("../extensions/localHelper");
11
+ const requirePermissions_1 = require("../requirePermissions");
12
+ const common_1 = require("../extensions/runtimes/common");
13
+ const error_1 = require("../error");
14
+ const displayExtensionInfo_1 = require("../extensions/displayExtensionInfo");
15
+ const refs = require("../extensions/refs");
16
+ const logger_1 = require("../logger");
17
+ const prompt_1 = require("../prompt");
18
+ const utils = require("../utils");
19
+ exports.command = new command_1.Command("ext:sdk:install <extensionName>")
20
+ .description("get an SDK for this extension. The SDK will be put in the 'generated' directory")
21
+ .option(`--codebase <codebase>`, `specifies a codebase to install the SDK into`)
22
+ .option(`--force`, `will overwrite existing sdk files if true`)
23
+ .before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extDevMinVersion")
24
+ .action(async (extensionName, options) => {
25
+ const runtime = await (0, common_1.getCodebaseRuntime)(options);
26
+ if (!runtime.startsWith("nodejs")) {
27
+ throw new error_1.FirebaseError(`Extension SDK generation is currently only supported for NodeJs. We detected the target source to be: ${runtime}`);
28
+ }
29
+ let spec;
30
+ let extensionRef;
31
+ let localPath;
32
+ if ((0, localHelper_1.isLocalExtension)(extensionName)) {
33
+ spec = await (0, localHelper_1.getLocalExtensionSpec)(extensionName);
34
+ spec.systemParams = [];
35
+ localPath = extensionName;
36
+ await (0, displayExtensionInfo_1.displayExtensionVersionInfo)({ spec });
37
+ }
38
+ else {
39
+ await (0, requirePermissions_1.requirePermissions)(options, ["firebaseextensions.sources.get"]);
40
+ await (0, extensionsHelper_1.ensureExtensionsApiEnabled)(options);
41
+ const hasPublisherId = extensionName.split("/").length >= 2;
42
+ if (hasPublisherId) {
43
+ const nameAndVersion = extensionName.split("/")[1];
44
+ if (nameAndVersion.split("@").length < 2) {
45
+ extensionName = extensionName + "@latest";
46
+ }
47
+ }
48
+ else {
49
+ const [name, version] = extensionName.split("@");
50
+ extensionName = `firebase/${name}@${version || "latest"}`;
51
+ }
52
+ const ref = refs.parse(extensionName);
53
+ const extension = await extensionsApi.getExtension(refs.toExtensionRef(ref));
54
+ const version = await extensionsApi.getExtensionVersion(extensionName);
55
+ spec = version.spec;
56
+ extensionRef = version.ref;
57
+ await (0, displayExtensionInfo_1.displayExtensionVersionInfo)({
58
+ spec,
59
+ extensionVersion: version,
60
+ latestApprovedVersion: extension.latestApprovedVersion,
61
+ latestVersion: extension.latestVersion,
62
+ });
63
+ if (version.state === "DEPRECATED") {
64
+ throw new error_1.FirebaseError(`Extension version ${clc.bold(extensionName)} is deprecated and cannot be installed. To install an SDK for the ` +
65
+ `latest non-deprecated version, omit the version in the extension ref.`);
66
+ }
67
+ logger_1.logger.info();
68
+ if ((extension.latestApprovedVersion &&
69
+ semver.gt(extension.latestApprovedVersion, version.spec.version)) ||
70
+ (!extension.latestApprovedVersion &&
71
+ extension.latestVersion &&
72
+ semver.gt(extension.latestVersion, version.spec.version))) {
73
+ const latest = extension.latestApprovedVersion || extension.latestVersion;
74
+ logger_1.logger.info(`You are about to install an SDK for extension version ${clc.bold(version.spec.version)} which is older than the latest ${extension.latestApprovedVersion ? "accepted version" : "version"} ${clc.bold(latest)}.`);
75
+ }
76
+ }
77
+ if (!(await (0, prompt_1.confirm)({
78
+ nonInteractive: options.nonInteractive,
79
+ force: options.force,
80
+ default: true,
81
+ }))) {
82
+ return;
83
+ }
84
+ const codeSample = await (0, common_1.writeSDK)(extensionRef, localPath, spec, options);
85
+ logger_1.logger.info();
86
+ utils.logSuccess("Extension SDK installed successfully");
87
+ logger_1.logger.info(codeSample);
88
+ });
@@ -23,6 +23,7 @@ exports.command = new command_1.Command("ext")
23
23
  "ext:configure",
24
24
  "ext:update",
25
25
  "ext:uninstall",
26
+ "ext:sdk:install",
26
27
  ];
27
28
  for (const commandName of commandNames) {
28
29
  const command = firebaseTools.getCommand(commandName);
@@ -83,6 +83,8 @@ function load(client) {
83
83
  client.ext.list = loadCommand("ext-list");
84
84
  client.ext.uninstall = loadCommand("ext-uninstall");
85
85
  client.ext.update = loadCommand("ext-update");
86
+ client.ext.sdk = {};
87
+ client.ext.sdk.install = loadCommand("ext-sdk-install");
86
88
  client.ext.dev = {};
87
89
  client.ext.dev.init = loadCommand("ext-dev-init");
88
90
  client.ext.dev.list = loadCommand("ext-dev-list");
@@ -12,7 +12,6 @@ async function ensureApis(projectId) {
12
12
  exports.ensureApis = ensureApis;
13
13
  async function ensureSparkApis(projectId) {
14
14
  const prefix = "dataconnect";
15
- await (0, ensureApiEnabled_1.ensure)(projectId, api.dataconnectOrigin(), prefix);
16
15
  await (0, ensureApiEnabled_1.ensure)(projectId, api.cloudSQLAdminOrigin(), prefix);
17
16
  }
18
17
  exports.ensureSparkApis = ensureSparkApis;
@@ -47,10 +47,11 @@ function configuresSummary(toConfigure) {
47
47
  : "";
48
48
  }
49
49
  exports.configuresSummary = configuresSummary;
50
- function deletesSummary(toDelete) {
50
+ function deletesSummary(toDelete, isDynamic) {
51
51
  const instancesToDelete = toDelete.map((s) => `\t${(0, exports.humanReadable)(s)}`).join("\n");
52
+ const definedLocation = isDynamic ? "your local source code" : "'firebase.json'";
52
53
  return toDelete.length
53
- ? `The following extension instances are not listed in 'firebase.json':\n${instancesToDelete}\n`
54
+ ? `The following extension instances are found in your project but do not exist in ${definedLocation}:\n${instancesToDelete}\n`
54
55
  : "";
55
56
  }
56
57
  exports.deletesSummary = deletesSummary;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveVersion = exports.want = exports.wantDynamic = exports.have = exports.getExtensionSpec = exports.getExtension = exports.getExtensionVersion = void 0;
3
+ exports.resolveVersion = exports.want = exports.wantDynamic = exports.have = exports.haveDynamic = 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");
@@ -49,9 +49,33 @@ async function getExtensionSpec(i) {
49
49
  return i.extensionSpec;
50
50
  }
51
51
  exports.getExtensionSpec = getExtensionSpec;
52
+ async function haveDynamic(projectId) {
53
+ return (await extensionsApi.listInstances(projectId))
54
+ .filter((i) => { var _a; return ((_a = i.labels) === null || _a === void 0 ? void 0 : _a.createdBy) === "SDK"; })
55
+ .map((i) => {
56
+ var _a;
57
+ const dep = {
58
+ instanceId: i.name.split("/").pop(),
59
+ params: i.config.params,
60
+ systemParams: (_a = i.config.systemParams) !== null && _a !== void 0 ? _a : {},
61
+ allowedEventTypes: i.config.allowedEventTypes,
62
+ eventarcChannel: i.config.eventarcChannel,
63
+ etag: i.etag,
64
+ labels: i.labels,
65
+ };
66
+ if (i.config.extensionRef) {
67
+ const ref = refs.parse(i.config.extensionRef);
68
+ dep.ref = ref;
69
+ dep.ref.version = i.config.extensionVersion;
70
+ }
71
+ return dep;
72
+ });
73
+ }
74
+ exports.haveDynamic = haveDynamic;
52
75
  async function have(projectId) {
53
- const instances = await extensionsApi.listInstances(projectId);
54
- return instances.map((i) => {
76
+ return (await extensionsApi.listInstances(projectId))
77
+ .filter((i) => { var _a; return !(((_a = i.labels) === null || _a === void 0 ? void 0 : _a.createdBy) === "SDK"); })
78
+ .map((i) => {
55
79
  var _a;
56
80
  const dep = {
57
81
  instanceId: i.name.split("/").pop(),
@@ -61,6 +85,9 @@ async function have(projectId) {
61
85
  eventarcChannel: i.config.eventarcChannel,
62
86
  etag: i.etag,
63
87
  };
88
+ if (i.labels) {
89
+ dep.labels = i.labels;
90
+ }
64
91
  if (i.config.extensionRef) {
65
92
  const ref = refs.parse(i.config.extensionRef);
66
93
  dep.ref = ref;
@@ -97,6 +124,7 @@ async function wantDynamic(args) {
97
124
  systemParams,
98
125
  allowedEventTypes,
99
126
  eventarcChannel,
127
+ labels: ext.labels,
100
128
  });
101
129
  }
102
130
  else if (ext.ref) {
@@ -107,6 +135,7 @@ async function wantDynamic(args) {
107
135
  systemParams,
108
136
  allowedEventTypes,
109
137
  eventarcChannel,
138
+ labels: ext.labels,
110
139
  });
111
140
  }
112
141
  }
@@ -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, noDeleteExtensions, isPrimaryCall) {
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))) && !(noDeleteExtensions === null || noDeleteExtensions === void 0 ? void 0 : noDeleteExtensions.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,9 +76,9 @@ 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));
79
+ logger_1.logger.info(deploymentSummary.deletesSummary(payload.instancesToDelete, isDynamic));
84
80
  if (options.dryRun) {
85
- logger_1.logger.info("On your next deploy, these you will be asked if you want to delete these instances.");
81
+ logger_1.logger.info("On your next deploy, you will be asked if you want to delete these instances.");
86
82
  logger_1.logger.info("If you deploy --force, they will be deleted.");
87
83
  }
88
84
  if (!options.dryRun &&
@@ -114,56 +110,23 @@ async function prepareHelper(context, options, payload, wantExtensions, noDelete
114
110
  async function prepareDynamicExtensions(context, options, payload, builds) {
115
111
  const filters = (0, functionsDeployHelper_1.getEndpointFilters)(options);
116
112
  const extensions = (0, common_1.extractExtensionsFromBuilds)(builds, filters);
117
- const isApiEnabled = await (0, extensionsHelper_1.checkExtensionsApiEnabled)(options);
118
- if (Object.keys(extensions).length === 0 && !isApiEnabled) {
119
- return;
120
- }
121
113
  const projectId = (0, projectUtils_1.needProjectId)(options);
122
114
  const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
123
- const aliases = (0, projectUtils_1.getAliases)(options, projectId);
124
- const projectDir = options.config.projectDir;
125
- const isPrimaryCall = !!options.only && !options.only.split(",").includes("extensions");
126
115
  await (0, extensionsHelper_1.ensureExtensionsApiEnabled)(options);
127
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
+ }
128
122
  const dynamicWant = await planner.wantDynamic({
129
123
  projectId,
130
124
  projectNumber,
131
125
  extensions,
132
126
  });
133
- let noDeleteExtensions = [];
134
- if (isPrimaryCall) {
135
- const firebaseJsonWant = await planner.want({
136
- projectId,
137
- projectNumber,
138
- aliases,
139
- projectDir,
140
- extensions: options.config.get("extensions", {}),
141
- });
142
- noDeleteExtensions = noDeleteExtensions.concat(firebaseJsonWant);
143
- if (hasNonDeployingCodebases(options)) {
144
- const dynamicAll = await planner.wantDynamic({
145
- projectId,
146
- projectNumber,
147
- extensions: await (0, common_1.extractAllDynamicExtensions)(options),
148
- });
149
- noDeleteExtensions = noDeleteExtensions.concat(dynamicAll);
150
- }
151
- }
152
- return prepareHelper(context, options, payload, dynamicWant, noDeleteExtensions, isPrimaryCall);
127
+ return prepareHelper(context, options, payload, dynamicWant, haveExtensions, true);
153
128
  }
154
129
  exports.prepareDynamicExtensions = prepareDynamicExtensions;
155
- function hasNonDeployingCodebases(options) {
156
- const functionFilters = (0, functionsDeployHelper_1.getEndpointFilters)(options);
157
- if (functionFilters === null || functionFilters === void 0 ? void 0 : functionFilters.length) {
158
- return true;
159
- }
160
- const functionsConfig = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions);
161
- const allCodebases = (0, functionsDeployHelper_1.targetCodebases)(functionsConfig);
162
- const deployingCodebases = (0, functionsDeployHelper_1.targetCodebases)(functionsConfig, functionFilters);
163
- if (allCodebases.length > deployingCodebases.length) {
164
- return true;
165
- }
166
- }
167
130
  async function prepare(context, options, payload) {
168
131
  context.extensionsStartTime = Date.now();
169
132
  const projectId = (0, projectUtils_1.needProjectId)(options);
@@ -172,19 +135,15 @@ async function prepare(context, options, payload) {
172
135
  const projectDir = options.config.projectDir;
173
136
  await (0, extensionsHelper_1.ensureExtensionsApiEnabled)(options);
174
137
  await (0, requirePermissions_1.requirePermissions)(options, ["firebaseextensions.instances.list"]);
175
- const firebaseJsonWant = await planner.want({
138
+ const wantExtensions = await planner.want({
176
139
  projectId,
177
140
  projectNumber,
178
141
  aliases,
179
142
  projectDir,
180
143
  extensions: options.config.get("extensions", {}),
181
144
  });
182
- const dynamicWant = await planner.wantDynamic({
183
- projectId,
184
- projectNumber,
185
- extensions: await (0, common_1.extractAllDynamicExtensions)(options),
186
- });
187
- return prepareHelper(context, options, payload, firebaseJsonWant, dynamicWant, true);
145
+ const haveExtensions = await planner.have(projectId);
146
+ return prepareHelper(context, options, payload, wantExtensions, haveExtensions, false);
188
147
  }
189
148
  exports.prepare = prepare;
190
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 creation of (_a = payload.instancesToCreate) !== null && _a !== void 0 ? _a : []) {
28
- const task = tasks.createExtensionInstanceTask(projectId, creation);
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 update of (_b = payload.instancesToUpdate) !== null && _b !== void 0 ? _b : []) {
32
- const task = tasks.updateExtensionInstanceTask(projectId, update);
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 update of (_c = payload.instancesToConfigure) !== null && _c !== void 0 ? _c : []) {
36
- const task = tasks.configureExtensionInstanceTask(projectId, update);
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 deletion of (_d = payload.instancesToDelete) !== null && _d !== void 0 ? _d : []) {
40
- const task = tasks.deleteExtensionInstanceTask(projectId, deletion);
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 newHave = await planner.have(projectId);
57
- (0, etags_1.saveEtags)(options.rc, projectId, newHave);
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
- await extensionsApi.createInstance({
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
- const extensionSource = await (0, extensionsHelper_1.createSourceFromLocation)(projectId, instanceSpec.localPath);
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;
@@ -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)) {
@@ -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 = "3.20.0";
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))) {
@@ -46,20 +46,20 @@ const EMULATOR_UPDATE_DETAILS = {
46
46
  },
47
47
  dataconnect: process.platform === "darwin"
48
48
  ? {
49
- version: "1.3.7",
50
- expectedSize: 25019136,
51
- expectedChecksum: "648ca3e289db8209c2d555bb381e5e5e",
49
+ version: "1.3.8",
50
+ expectedSize: 25027328,
51
+ expectedChecksum: "903f0e8dd8212fec575dc2eba34be6e7",
52
52
  }
53
53
  : process.platform === "win32"
54
54
  ? {
55
- version: "1.3.7",
56
- expectedSize: 25441792,
57
- expectedChecksum: "16081147aa94f1b8691329d5b9430b69",
55
+ version: "1.3.8",
56
+ expectedSize: 25450496,
57
+ expectedChecksum: "e09999deda7301eba06f88face5e1744",
58
58
  }
59
59
  : {
60
- version: "1.3.7",
61
- expectedSize: 24928408,
62
- expectedChecksum: "8749930f3a43f616e3ae231af8c787a8",
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.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
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.AUTOPOULATED_PARAM_PLACEHOLDERS = {
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.AUTOPOULATED_PARAM_PLACEHOLDERS);
452
+ subbedSpec.params = substituteParams(extensionSpec.params || [], exports.AUTOPOPULATED_PARAM_PLACEHOLDERS);
453
453
  validateSpec(subbedSpec);
454
454
  return extensionSpec;
455
455
  }