firebase-tools 10.4.2 → 10.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/lib/bin/firebase.js +1 -1
  2. package/lib/command.js +4 -4
  3. package/lib/commands/deploy.js +1 -1
  4. package/lib/commands/emulators-start.js +13 -3
  5. package/lib/commands/ext-configure.js +15 -5
  6. package/lib/commands/ext-dev-emulators-start.js +5 -1
  7. package/lib/commands/ext-export.js +6 -5
  8. package/lib/commands/ext-install.js +28 -44
  9. package/lib/commands/ext-update.js +9 -1
  10. package/lib/commands/functions-delete.js +2 -5
  11. package/lib/commands/functions-secrets-destroy.js +23 -3
  12. package/lib/commands/functions-secrets-prune.js +15 -12
  13. package/lib/commands/functions-secrets-set.js +51 -4
  14. package/lib/commands/hosting-channel-deploy.js +2 -2
  15. package/lib/deploy/database/deploy.js +4 -0
  16. package/lib/deploy/database/index.js +1 -0
  17. package/lib/deploy/extensions/deploy.js +4 -4
  18. package/lib/deploy/extensions/deploymentSummary.js +8 -5
  19. package/lib/deploy/extensions/planner.js +36 -9
  20. package/lib/deploy/extensions/prepare.js +1 -1
  21. package/lib/deploy/extensions/secrets.js +2 -2
  22. package/lib/deploy/extensions/tasks.js +60 -21
  23. package/lib/deploy/functions/backend.js +17 -6
  24. package/lib/deploy/functions/build.js +162 -0
  25. package/lib/deploy/functions/checkIam.js +6 -5
  26. package/lib/deploy/functions/deploy.js +14 -15
  27. package/lib/deploy/functions/ensure.js +4 -4
  28. package/lib/deploy/functions/functionsDeployHelper.js +54 -23
  29. package/lib/deploy/functions/prepare.js +92 -39
  30. package/lib/deploy/functions/prepareFunctionsUpload.js +16 -21
  31. package/lib/deploy/functions/pricing.js +6 -3
  32. package/lib/deploy/functions/prompts.js +1 -7
  33. package/lib/deploy/functions/release/fabricator.js +44 -5
  34. package/lib/deploy/functions/release/index.js +31 -6
  35. package/lib/deploy/functions/release/planner.js +10 -8
  36. package/lib/deploy/functions/release/reporter.js +14 -11
  37. package/lib/deploy/functions/runtimes/discovery/parsing.js +12 -6
  38. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +61 -13
  39. package/lib/deploy/functions/runtimes/node/index.js +1 -1
  40. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +3 -3
  41. package/lib/deploy/functions/runtimes/node/parseTriggers.js +29 -24
  42. package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
  43. package/lib/deploy/functions/services/auth.js +95 -0
  44. package/lib/deploy/functions/services/index.js +41 -21
  45. package/lib/deploy/functions/services/storage.js +1 -6
  46. package/lib/deploy/functions/validate.js +8 -5
  47. package/lib/deploy/hosting/args.js +2 -0
  48. package/lib/deploy/hosting/convertConfig.js +37 -8
  49. package/lib/deploy/hosting/deploy.js +3 -3
  50. package/lib/deploy/hosting/prepare.js +2 -2
  51. package/lib/deploy/hosting/release.js +6 -2
  52. package/lib/deploy/index.js +82 -93
  53. package/lib/deploy/remoteconfig/deploy.js +4 -0
  54. package/lib/deploy/remoteconfig/index.js +3 -1
  55. package/lib/emulator/auth/operations.js +26 -20
  56. package/lib/emulator/auth/state.js +79 -43
  57. package/lib/emulator/auth/utils.js +3 -25
  58. package/lib/emulator/commandUtils.js +72 -2
  59. package/lib/emulator/controller.js +14 -5
  60. package/lib/emulator/downloadableEmulators.js +47 -24
  61. package/lib/emulator/extensions/postinstall.js +41 -0
  62. package/lib/emulator/extensions/validation.js +2 -2
  63. package/lib/emulator/extensionsEmulator.js +85 -21
  64. package/lib/emulator/functionsEmulator.js +79 -7
  65. package/lib/emulator/functionsEmulatorShared.js +36 -21
  66. package/lib/emulator/registry.js +34 -12
  67. package/lib/emulator/shared/request.js +19 -0
  68. package/lib/emulator/storage/apis/firebase.js +32 -35
  69. package/lib/emulator/storage/apis/gcloud.js +84 -66
  70. package/lib/emulator/storage/files.js +56 -52
  71. package/lib/emulator/storage/index.js +23 -3
  72. package/lib/emulator/storage/metadata.js +18 -8
  73. package/lib/emulator/storage/rules/manager.js +7 -17
  74. package/lib/emulator/storage/rules/utils.js +11 -3
  75. package/lib/emulator/storage/server.js +38 -12
  76. package/lib/ensureApiEnabled.js +8 -4
  77. package/lib/extensions/askUserForParam.js +14 -11
  78. package/lib/extensions/changelog.js +1 -1
  79. package/lib/extensions/emulator/optionsHelper.js +9 -10
  80. package/lib/extensions/emulator/specHelper.js +7 -1
  81. package/lib/extensions/emulator/triggerHelper.js +11 -14
  82. package/lib/extensions/extensionsApi.js +2 -1
  83. package/lib/extensions/extensionsHelper.js +30 -24
  84. package/lib/extensions/manifest.js +28 -8
  85. package/lib/extensions/paramHelper.js +19 -13
  86. package/lib/extensions/provisioningHelper.js +2 -2
  87. package/lib/extensions/warnings.js +3 -3
  88. package/lib/functions/env.js +10 -2
  89. package/lib/functions/events/index.js +7 -0
  90. package/lib/functions/events/v1.js +6 -0
  91. package/lib/functions/projectConfig.js +24 -3
  92. package/lib/functions/runtimeConfigExport.js +10 -6
  93. package/lib/functions/secrets.js +99 -6
  94. package/lib/gcp/cloudfunctions.js +37 -18
  95. package/lib/gcp/cloudfunctionsv2.js +41 -25
  96. package/lib/gcp/cloudtasks.js +5 -3
  97. package/lib/gcp/identityPlatform.js +44 -0
  98. package/lib/gcp/secretManager.js +2 -2
  99. package/lib/metaprogramming.js +2 -0
  100. package/lib/previews.js +1 -1
  101. package/lib/serve/hosting.js +25 -12
  102. package/lib/serve/index.js +6 -0
  103. package/lib/track.js +15 -21
  104. package/lib/utils.js +30 -1
  105. package/npm-shrinkwrap.json +44 -2
  106. package/package.json +4 -1
  107. package/schema/firebase-config.json +6 -0
  108. package/lib/emulator/storage/list.js +0 -18
@@ -20,7 +20,7 @@ marked.setOptions({
20
20
  renderer: new TerminalRenderer(),
21
21
  });
22
22
  const updateMessage = `Update available ${clc.xterm(240)("{currentVersion}")} → ${clc.green("{latestVersion}")}\n` +
23
- `To update to the latest version using npm, run ${clc.cyan("npm install -g firebase-tools")}\n` +
23
+ `To update to the latest version using npm, run\n${clc.cyan("npm install -g firebase-tools")}\n` +
24
24
  `For other CLI management options, visit the ${marked("[CLI documentation](https://firebase.google.com/docs/cli#update-cli)")}`;
25
25
  updateNotifier.notify({ defer: true, isGlobal: true, message: updateMessage });
26
26
  const client = require("..");
package/lib/command.js CHANGED
@@ -9,7 +9,7 @@ const rc_1 = require("./rc");
9
9
  const config_1 = require("./config");
10
10
  const configstore_1 = require("./configstore");
11
11
  const detectProjectRoot_1 = require("./detectProjectRoot");
12
- const track = require("./track");
12
+ const track_1 = require("./track");
13
13
  const clc = require("cli-color");
14
14
  const auth_1 = require("./auth");
15
15
  const projects_1 = require("./management/projects");
@@ -85,7 +85,7 @@ class Command {
85
85
  }, null, 2));
86
86
  }
87
87
  const duration = new Date().getTime() - start;
88
- void track(this.name, "success", duration).then(() => process.exit());
88
+ void (0, track_1.track)(this.name, "success", duration).then(() => process.exit());
89
89
  })
90
90
  .catch(async (err) => {
91
91
  if ((0, utils_1.getInheritedOption)(options, "json")) {
@@ -96,7 +96,7 @@ class Command {
96
96
  }
97
97
  const duration = Date.now() - start;
98
98
  const errorEvent = err.exit === 1 ? "Error (User)" : "Error (Unexpected)";
99
- await Promise.all([track(this.name, "error", duration), track(errorEvent, "", duration)]);
99
+ await Promise.all([(0, track_1.track)(this.name, "error", duration), (0, track_1.track)(errorEvent, "", duration)]);
100
100
  client.errorOut(err);
101
101
  });
102
102
  });
@@ -197,7 +197,7 @@ function validateProjectId(project) {
197
197
  if (PROJECT_ID_REGEX.test(project)) {
198
198
  return;
199
199
  }
200
- track("Project ID Check", "invalid");
200
+ (0, track_1.track)("Project ID Check", "invalid");
201
201
  const invalidMessage = "Invalid project id: " + clc.bold(project) + ".";
202
202
  if (project.toLowerCase() !== project) {
203
203
  throw new error_1.FirebaseError(invalidMessage + "\nNote: Project id must be all lowercase.");
@@ -5,7 +5,7 @@ const { requirePermissions } = require("../requirePermissions");
5
5
  const { checkServiceAccountIam } = require("../deploy/functions/checkIam");
6
6
  const checkValidTargetFilters = require("../checkValidTargetFilters");
7
7
  const { Command } = require("../command");
8
- const deploy = require("../deploy");
8
+ const { deploy } = require("../deploy");
9
9
  const { requireConfig } = require("../requireConfig");
10
10
  const { filterTargets } = require("../filterTargets");
11
11
  const { requireHostingSite } = require("../requireHostingSite");
@@ -8,6 +8,7 @@ const registry_1 = require("../emulator/registry");
8
8
  const types_1 = require("../emulator/types");
9
9
  const clc = require("cli-color");
10
10
  const constants_1 = require("../emulator/constants");
11
+ const utils_1 = require("../utils");
11
12
  const Table = require("cli-table");
12
13
  function stylizeLink(url) {
13
14
  return clc.underline(clc.bold(url));
@@ -22,8 +23,9 @@ module.exports = new command_1.Command("emulators:start")
22
23
  .option(commandUtils.FLAG_EXPORT_ON_EXIT, commandUtils.DESC_EXPORT_ON_EXIT)
23
24
  .action(async (options) => {
24
25
  const killSignalPromise = commandUtils.shutdownWhenKilled(options);
26
+ let deprecationNotices;
25
27
  try {
26
- await controller.startAll(options);
28
+ ({ deprecationNotices } = await controller.startAll(options));
27
29
  }
28
30
  catch (e) {
29
31
  await controller.cleanShutdown();
@@ -61,7 +63,7 @@ module.exports = new command_1.Command("emulators:start")
61
63
  .map((emulator) => {
62
64
  const emulatorName = constants_1.Constants.description(emulator).replace(/ emulator/i, "");
63
65
  const isSupportedByUi = types_1.EMULATORS_SUPPORTED_BY_UI.includes(emulator);
64
- const info = registry_1.EmulatorRegistry.getInfo(emulator === types_1.Emulators.EXTENSIONS ? types_1.Emulators.FUNCTIONS : emulator);
66
+ const info = registry_1.EmulatorRegistry.getInfo(emulator);
65
67
  if (!info) {
66
68
  return [emulatorName, "Failed to initialize (see above)", "", ""];
67
69
  }
@@ -75,6 +77,11 @@ module.exports = new command_1.Command("emulators:start")
75
77
  })
76
78
  .map((col) => col.slice(0, head.length))
77
79
  .filter((v) => v));
80
+ let extensionsTable = "";
81
+ if (registry_1.EmulatorRegistry.isRunning(types_1.Emulators.EXTENSIONS)) {
82
+ const extensionsEmulatorInstance = registry_1.EmulatorRegistry.get(types_1.Emulators.EXTENSIONS);
83
+ extensionsTable = extensionsEmulatorInstance.extensionsInfoTable(options);
84
+ }
78
85
  logger_1.logger.info(`\n${successMessageTable}
79
86
 
80
87
  ${emulatorsTable}
@@ -82,8 +89,11 @@ ${hubInfo
82
89
  ? clc.blackBright(" Emulator Hub running at ") + registry_1.EmulatorRegistry.getInfoHostString(hubInfo)
83
90
  : clc.blackBright(" Emulator Hub not running.")}
84
91
  ${clc.blackBright(" Other reserved ports:")} ${reservedPortsString}
85
-
92
+ ${extensionsTable}
86
93
  Issues? Report them at ${stylizeLink("https://github.com/firebase/firebase-tools/issues")} and attach the *-debug.log files.
87
94
  `);
95
+ for (const notice of deprecationNotices) {
96
+ (0, utils_1.logLabeledWarning)("emulators", notice, "warn");
97
+ }
88
98
  await killSignalPromise;
89
99
  });
@@ -41,13 +41,22 @@ exports.default = new command_1.Command("ext:configure <extensionInstanceId>")
41
41
  throw new error_1.FirebaseError(`Command not supported in non-interactive mode, edit ./extensions/${instanceId}.env directly instead`);
42
42
  }
43
43
  const config = manifest.loadConfig(options);
44
- const targetRef = manifest.getInstanceRef(instanceId, config);
45
- const extensionVersion = await extensionsApi.getExtensionVersion(refs.toExtensionVersionRef(targetRef));
44
+ const refOrPath = manifest.getInstanceTarget(instanceId, config);
45
+ const isLocalSource = (0, extensionsHelper_1.isLocalPath)(refOrPath);
46
+ let spec;
47
+ if (isLocalSource) {
48
+ const source = await (0, extensionsHelper_1.createSourceFromLocation)((0, projectUtils_1.needProjectId)({ projectId }), refOrPath);
49
+ spec = source.spec;
50
+ }
51
+ else {
52
+ const extensionVersion = await extensionsApi.getExtensionVersion(refOrPath);
53
+ spec = extensionVersion.spec;
54
+ }
46
55
  const oldParamValues = manifest.readInstanceParam({
47
56
  instanceId,
48
57
  projectDir: config.projectDir,
49
58
  });
50
- const [immutableParams, tbdParams] = (0, functional_1.partition)(extensionVersion.spec.params, (param) => { var _a; return (_a = param.immutable) !== null && _a !== void 0 ? _a : false; });
59
+ const [immutableParams, tbdParams] = (0, functional_1.partition)(spec.params, (param) => { var _a; return (_a = param.immutable) !== null && _a !== void 0 ? _a : false; });
51
60
  infoImmutableParams(immutableParams, oldParamValues);
52
61
  paramHelper.setNewDefaults(tbdParams, oldParamValues);
53
62
  const mutableParamsBindingOptions = await paramHelper.getParams({
@@ -62,9 +71,10 @@ exports.default = new command_1.Command("ext:configure <extensionInstanceId>")
62
71
  await manifest.writeToManifest([
63
72
  {
64
73
  instanceId,
65
- ref: targetRef,
74
+ ref: !isLocalSource ? refs.parse(refOrPath) : undefined,
75
+ localPath: isLocalSource ? refOrPath : undefined,
66
76
  params: newParamOptions,
67
- paramSpecs: extensionVersion.spec.params,
77
+ extensionSpec: spec,
68
78
  },
69
79
  ], config, {
70
80
  nonInteractive: false,
@@ -19,9 +19,10 @@ module.exports = new command_1.Command("ext:dev:emulators:start")
19
19
  .action(async (options) => {
20
20
  const killSignalPromise = commandUtils.shutdownWhenKilled(options);
21
21
  const emulatorOptions = await optionsHelper.buildOptions(options);
22
+ let deprecationNotices;
22
23
  try {
23
24
  commandUtils.beforeEmulatorCommand(emulatorOptions);
24
- await controller.startAll(emulatorOptions);
25
+ ({ deprecationNotices } = await controller.startAll(emulatorOptions));
25
26
  }
26
27
  catch (e) {
27
28
  await controller.cleanShutdown();
@@ -31,5 +32,8 @@ module.exports = new command_1.Command("ext:dev:emulators:start")
31
32
  throw e;
32
33
  }
33
34
  utils.logSuccess("All emulators ready, it is now safe to connect.");
35
+ for (const notice of deprecationNotices) {
36
+ utils.logLabeledWarning("emulators", notice, "warn");
37
+ }
34
38
  await killSignalPromise;
35
39
  });
@@ -22,16 +22,17 @@ module.exports = new command_1.Command("ext:export")
22
22
  .action(async (options) => {
23
23
  const projectId = (0, projectUtils_1.needProjectId)(options);
24
24
  const projectNumber = await (0, getProjectNumber_1.getProjectNumber)(options);
25
- const have = await Promise.all((await planner.have(projectId)).map(async (i) => {
26
- const subbed = await (0, export_1.setSecretParamsToLatest)(i);
27
- return (0, export_1.parameterizeProject)(projectId, projectNumber, subbed);
28
- }));
25
+ const have = await Promise.all(await planner.have(projectId));
29
26
  if (have.length === 0) {
30
27
  logger_1.logger.info(`No extension instances installed on ${projectId}, so there is nothing to export.`);
31
28
  return;
32
29
  }
33
30
  const [withRef, withoutRef] = (0, functional_1.partition)(have, (s) => !!s.ref);
34
- (0, export_1.displayExportInfo)(withRef, withoutRef);
31
+ const withRefSubbed = await Promise.all(withRef.map(async (i) => {
32
+ const subbed = await (0, export_1.setSecretParamsToLatest)(i);
33
+ return (0, export_1.parameterizeProject)(projectId, projectNumber, subbed);
34
+ }));
35
+ (0, export_1.displayExportInfo)(withRefSubbed, withoutRef);
35
36
  if (!options.nonInteractive &&
36
37
  !options.force &&
37
38
  !(await (0, prompt_1.promptOnce)({
@@ -46,7 +46,7 @@ exports.default = new command_1.Command("ext:install [extensionName]")
46
46
  .before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
47
47
  .before(extensionsHelper_1.diagnoseAndFixProject)
48
48
  .action(async (extensionName, options) => {
49
- var _a;
49
+ var _a, _b;
50
50
  const projectId = (0, projectUtils_1.getProjectId)(options);
51
51
  const paramsEnvPath = ((_a = options.params) !== null && _a !== void 0 ? _a : "");
52
52
  let learnMore = false;
@@ -62,20 +62,23 @@ exports.default = new command_1.Command("ext:install [extensionName]")
62
62
  }
63
63
  }
64
64
  let source;
65
- let extVersion;
65
+ let extensionVersion;
66
66
  if ((0, extensionsHelper_1.isUrlPath)(extensionName)) {
67
- void (0, track_1.track)("Extension Install", "Install by url path", options.interactive ? 1 : 0);
67
+ throw new error_1.FirebaseError(`Installing with a source url is no longer supported in the CLI. Please use Firebase Console instead.`);
68
68
  }
69
- if ((0, extensionsHelper_1.isLocalOrURLPath)(extensionName)) {
69
+ if ((0, extensionsHelper_1.isLocalPath)(extensionName)) {
70
+ source = await (0, extensionsHelper_1.createSourceFromLocation)((0, projectUtils_1.needProjectId)({ projectId }), extensionName);
71
+ (0, displayExtensionInfo_1.displayExtInfo)(extensionName, "", source.spec);
70
72
  void (0, track_1.track)("Extension Install", "Install by Source", options.interactive ? 1 : 0);
71
- if (options.local) {
72
- throw new error_1.FirebaseError("Installing a local source locally is not supported yet, please use ext:dev:emulator commands");
73
- }
74
- source = await infoInstallBySource((0, projectUtils_1.needProjectId)({ projectId }), extensionName);
75
73
  }
76
74
  else {
77
75
  void (0, track_1.track)("Extension Install", "Install by Extension Ref", options.interactive ? 1 : 0);
78
- extVersion = await infoInstallByReference(extensionName, options.interactive);
76
+ extensionName = (0, extensionsHelper_1.canonicalizeRefInput)(extensionName);
77
+ extensionVersion = await extensionsApi.getExtensionVersion(extensionName);
78
+ await infoExtensionVersion({
79
+ extensionName,
80
+ extensionVersion,
81
+ });
79
82
  }
80
83
  if (!(await (0, extensionsHelper_1.confirm)({
81
84
  nonInteractive: options.nonInteractive,
@@ -84,10 +87,10 @@ exports.default = new command_1.Command("ext:install [extensionName]")
84
87
  }))) {
85
88
  return;
86
89
  }
87
- if (!source && !extVersion) {
90
+ if (!source && !extensionVersion) {
88
91
  throw new error_1.FirebaseError("Could not find a source. Please specify a valid source to continue.");
89
92
  }
90
- const spec = (source === null || source === void 0 ? void 0 : source.spec) || (extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec);
93
+ const spec = (_b = source === null || source === void 0 ? void 0 : source.spec) !== null && _b !== void 0 ? _b : extensionVersion === null || extensionVersion === void 0 ? void 0 : extensionVersion.spec;
91
94
  if (!spec) {
92
95
  throw new error_1.FirebaseError(`Could not find the extension.yaml for extension '${clc.bold(extensionName)}'. Please make sure this is a valid extension and try again.`);
93
96
  }
@@ -103,7 +106,7 @@ exports.default = new command_1.Command("ext:install [extensionName]")
103
106
  projectId,
104
107
  extensionName,
105
108
  source,
106
- extVersion,
109
+ extVersion: extensionVersion,
107
110
  nonInteractive: options.nonInteractive,
108
111
  force: options.force,
109
112
  });
@@ -123,7 +126,7 @@ exports.default = new command_1.Command("ext:install [extensionName]")
123
126
  projectId: projectId,
124
127
  extensionName,
125
128
  source,
126
- extVersion,
129
+ extVersion: extensionVersion,
127
130
  nonInteractive: options.nonInteractive,
128
131
  force: options.force,
129
132
  });
@@ -137,37 +140,17 @@ exports.default = new command_1.Command("ext:install [extensionName]")
137
140
  throw err;
138
141
  }
139
142
  });
140
- async function infoInstallBySource(projectId, extensionName) {
141
- let source;
142
- try {
143
- source = await (0, extensionsHelper_1.createSourceFromLocation)(projectId, extensionName);
144
- }
145
- catch (err) {
146
- throw new error_1.FirebaseError(`Unable to find published extension '${clc.bold(extensionName)}', ` +
147
- `and encountered the following error when trying to create an instance of extension '${clc.bold(extensionName)}':\n ${err.message}`);
148
- }
149
- (0, displayExtensionInfo_1.displayExtInfo)(extensionName, "", source.spec);
150
- return source;
151
- }
152
- async function infoInstallByReference(extensionName, interactive) {
153
- if (extensionName.split("/").length < 2) {
154
- const [extensionID, version] = extensionName.split("@");
155
- extensionName = `firebase/${extensionID}@${version || "latest"}`;
156
- }
157
- const ref = refs.parse(extensionName);
143
+ async function infoExtensionVersion(args) {
144
+ const ref = refs.parse(args.extensionName);
158
145
  const extension = await extensionsApi.getExtension(refs.toExtensionRef(ref));
159
- if (!ref.version) {
160
- void (0, track_1.track)("Extension Install", "Install by Extension Version Ref", interactive ? 1 : 0);
161
- extensionName = `${extensionName}@latest`;
162
- }
163
- const extVersion = await extensionsApi.getExtensionVersion(extensionName);
164
- (0, displayExtensionInfo_1.displayExtInfo)(extensionName, ref.publisherId, extVersion.spec, true);
165
- await (0, warnings_1.displayWarningPrompts)(ref.publisherId, extension.registryLaunchStage, extVersion);
166
- return extVersion;
146
+ (0, displayExtensionInfo_1.displayExtInfo)(args.extensionName, ref.publisherId, args.extensionVersion.spec, true);
147
+ await (0, warnings_1.displayWarningPrompts)(ref.publisherId, extension.registryLaunchStage, args.extensionVersion);
167
148
  }
168
149
  async function installToManifest(options) {
169
- const { projectId, extensionName, extVersion, paramsEnvPath, nonInteractive, force } = options;
170
- const spec = extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec;
150
+ var _a;
151
+ const { projectId, extensionName, extVersion, source, paramsEnvPath, nonInteractive, force } = options;
152
+ const isLocalSource = (0, extensionsHelper_1.isLocalPath)(extensionName);
153
+ const spec = (_a = extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec) !== null && _a !== void 0 ? _a : source === null || source === void 0 ? void 0 : source.spec;
171
154
  if (!spec) {
172
155
  throw new error_1.FirebaseError(`Could not find the extension.yaml for ${extensionName}. Please make sure this is a valid extension and try again.`);
173
156
  }
@@ -183,13 +166,14 @@ async function installToManifest(options) {
183
166
  paramsEnvPath,
184
167
  instanceId,
185
168
  });
186
- const ref = refs.parse(extVersion.ref);
169
+ const ref = extVersion ? refs.parse(extVersion.ref) : undefined;
187
170
  await manifest.writeToManifest([
188
171
  {
189
172
  instanceId,
190
- ref,
173
+ ref: !isLocalSource ? ref : undefined,
174
+ localPath: isLocalSource ? extensionName : undefined,
191
175
  params: paramBindingOptions,
192
- paramSpecs: spec.params,
176
+ extensionSpec: spec,
193
177
  },
194
178
  ], config, { nonInteractive, force: force !== null && force !== void 0 ? force : false });
195
179
  manifest.showPreviewWarning();
@@ -45,6 +45,13 @@ exports.default = new command_1.Command("ext:update <extensionInstanceId> [updat
45
45
  if (options.local) {
46
46
  const projectId = (0, projectUtils_1.getProjectId)(options);
47
47
  const config = manifest.loadConfig(options);
48
+ const oldRefOrPath = manifest.getInstanceTarget(instanceId, config);
49
+ if ((0, extensionsHelper_1.isLocalPath)(oldRefOrPath)) {
50
+ throw new error_1.FirebaseError(`Updating an extension with local source is not neccessary. ` +
51
+ `Rerun "firebase deploy" or restart the emulator after making changes to your local extension source. ` +
52
+ `If you've edited the extension param spec, you can edit an extension instance's params ` +
53
+ `interactively by running "firebase ext:configure --local {instance-id}"`);
54
+ }
48
55
  const oldRef = manifest.getInstanceRef(instanceId, config);
49
56
  const oldExtensionVersion = await extensionsApi.getExtensionVersion(refs.toExtensionVersionRef(oldRef));
50
57
  updateSource = (0, updateHelper_1.inferUpdateSource)(updateSource, refs.toExtensionRef(oldRef));
@@ -84,7 +91,8 @@ exports.default = new command_1.Command("ext:update <extensionInstanceId> [updat
84
91
  instanceId,
85
92
  ref: refs.parse(newExtensionVersion.ref),
86
93
  params: newParamBindingOptions,
87
- paramSpecs: newExtensionVersion.spec.params,
94
+ extensionSpec: newExtensionVersion.spec,
95
+ extensionVersion: newExtensionVersion,
88
96
  },
89
97
  ], config, {
90
98
  nonInteractive: options.nonInteractive,
@@ -29,7 +29,7 @@ exports.default = new command_1.Command("functions:delete [filters...]")
29
29
  }
30
30
  const context = {
31
31
  projectId: (0, projectUtils_1.needProjectId)(options),
32
- filters: filters.map((f) => f.split(".")),
32
+ filters: filters.map((f) => ({ idChunks: f.split(".") })),
33
33
  };
34
34
  const [config, existingBackend] = await Promise.all([
35
35
  functionsConfig.getFirebaseConfig(options),
@@ -40,10 +40,7 @@ exports.default = new command_1.Command("functions:delete [filters...]")
40
40
  if (options.region) {
41
41
  existingBackend.endpoints = { [options.region]: existingBackend.endpoints[options.region] };
42
42
  }
43
- const plan = planner.createDeploymentPlan(backend.empty(), existingBackend, {
44
- filters: context.filters,
45
- deleteAll: true,
46
- });
43
+ const plan = planner.createDeploymentPlan(backend.empty(), existingBackend, context.filters, true);
47
44
  const allEpToDelete = Object.values(plan)
48
45
  .map((changes) => changes.endpointsToDelete)
49
46
  .reduce(functional_1.reduceFlat, [])
@@ -1,21 +1,41 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const command_1 = require("../command");
4
- const logger_1 = require("../logger");
5
4
  const projectUtils_1 = require("../projectUtils");
6
5
  const secretManager_1 = require("../gcp/secretManager");
7
6
  const prompt_1 = require("../prompt");
7
+ const utils_1 = require("../utils");
8
8
  const secrets = require("../functions/secrets");
9
+ const backend = require("../deploy/functions/backend");
9
10
  exports.default = new command_1.Command("functions:secrets:destroy <KEY>[@version]")
10
11
  .description("Destroy a secret. Defaults to destroying the latest version.")
11
12
  .withForce("Destroys a secret without confirmation.")
12
13
  .action(async (key, options) => {
13
14
  const projectId = (0, projectUtils_1.needProjectId)(options);
15
+ const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
16
+ const haveBackend = await backend.existingBackend({ projectId });
14
17
  let [name, version] = key.split("@");
15
18
  if (!version) {
16
19
  version = "latest";
17
20
  }
18
21
  const sv = await (0, secretManager_1.getSecretVersion)(projectId, name, version);
22
+ if (sv.state === "DESTROYED") {
23
+ (0, utils_1.logBullet)(`Secret ${sv.secret.name}@${version} is already destroyed. Nothing to do.`);
24
+ return;
25
+ }
26
+ const boundEndpoints = backend
27
+ .allEndpoints(haveBackend)
28
+ .filter((e) => secrets.inUse({ projectId, projectNumber }, sv.secret, e));
29
+ if (boundEndpoints.length > 0) {
30
+ const endpointsMsg = boundEndpoints
31
+ .map((e) => `${e.id}[${e.platform}](${e.region})`)
32
+ .join("\t\n");
33
+ (0, utils_1.logWarning)(`Secret ${name}@${version} is currently in use by following functions:\n\t${endpointsMsg}`);
34
+ if (!options.force) {
35
+ (0, utils_1.logWarning)("Refusing to destroy secret in use. Use -f to destroy the secret anyway.");
36
+ return;
37
+ }
38
+ }
19
39
  if (!options.force) {
20
40
  const confirm = await (0, prompt_1.promptOnce)({
21
41
  name: "destroy",
@@ -28,12 +48,12 @@ exports.default = new command_1.Command("functions:secrets:destroy <KEY>[@versio
28
48
  }
29
49
  }
30
50
  await (0, secretManager_1.destroySecretVersion)(projectId, name, version);
31
- logger_1.logger.info(`Destroyed secret version ${name}@${sv.versionId}`);
51
+ (0, utils_1.logBullet)(`Destroyed secret version ${name}@${sv.versionId}`);
32
52
  const secret = await (0, secretManager_1.getSecret)(projectId, name);
33
53
  if (secrets.isFirebaseManaged(secret)) {
34
54
  const versions = await (0, secretManager_1.listSecretVersions)(projectId, name);
35
55
  if (versions.filter((v) => v.state === "ENABLED").length === 0) {
36
- logger_1.logger.info(`No active secret versions left. Destroying secret ${name}`);
56
+ (0, utils_1.logBullet)(`No active secret versions left. Destroying secret ${name}`);
37
57
  await (0, secretManager_1.deleteSecret)(projectId, name);
38
58
  }
39
59
  }
@@ -10,6 +10,7 @@ const utils_1 = require("../utils");
10
10
  const prompt_1 = require("../prompt");
11
11
  const secretManager_1 = require("../gcp/secretManager");
12
12
  exports.default = new command_1.Command("functions:secrets:prune")
13
+ .withForce("Destroys unused secrets without prompt")
13
14
  .description("Destroys unused secrets")
14
15
  .before(requirePermissions_1.requirePermissions, [
15
16
  "cloudfunctions.functions.list",
@@ -32,18 +33,20 @@ exports.default = new command_1.Command("functions:secrets:prune")
32
33
  }
33
34
  (0, utils_1.logBullet)(`Found ${pruned.length} unused active secret versions:\n\t` +
34
35
  pruned.map((sv) => `${sv.secret}@${sv.version}`).join("\n\t"));
35
- const confirm = await (0, prompt_1.promptOnce)({
36
- name: "destroy",
37
- type: "confirm",
38
- default: true,
39
- message: `Do you want to destroy unused secret versions?`,
40
- }, options);
41
- if (!confirm) {
42
- (0, utils_1.logBullet)("Run the following commands to destroy each unused secret version:\n\t" +
43
- pruned
44
- .map((sv) => `firebase functions:secrets:destroy ${sv.secret}@${sv.version}`)
45
- .join("\n\t"));
46
- return;
36
+ if (!options.force) {
37
+ const confirm = await (0, prompt_1.promptOnce)({
38
+ name: "destroy",
39
+ type: "confirm",
40
+ default: true,
41
+ message: `Do you want to destroy unused secret versions?`,
42
+ }, options);
43
+ if (!confirm) {
44
+ (0, utils_1.logBullet)("Run the following commands to destroy each unused secret version:\n\t" +
45
+ pruned
46
+ .map((sv) => `firebase functions:secrets:destroy ${sv.secret}@${sv.version}`)
47
+ .join("\n\t"));
48
+ return;
49
+ }
47
50
  }
48
51
  await Promise.all(pruned.map((sv) => (0, secretManager_1.destroySecretVersion)(projectId, sv.secret, sv.version)));
49
52
  (0, utils_1.logSuccess)("Destroyed all unused secrets!");
@@ -10,9 +10,11 @@ const prompt_1 = require("../prompt");
10
10
  const utils_1 = require("../utils");
11
11
  const projectUtils_1 = require("../projectUtils");
12
12
  const secretManager_1 = require("../gcp/secretManager");
13
+ const secrets = require("../functions/secrets");
14
+ const backend = require("../deploy/functions/backend");
13
15
  exports.default = new command_1.Command("functions:secrets:set <KEY>")
14
- .description("Create or update a secret for use in Cloud Functions for Firebase")
15
- .withForce("Does not ensure input keys are valid or upgrade existing secrets to have Firebase manage them.")
16
+ .description("Create or update a secret for use in Cloud Functions for Firebase.")
17
+ .withForce("Automatically updates functions to use the new secret.")
16
18
  .before(requirePermissions_1.requirePermissions, [
17
19
  "secretmanager.secrets.create",
18
20
  "secretmanager.secrets.get",
@@ -22,6 +24,7 @@ exports.default = new command_1.Command("functions:secrets:set <KEY>")
22
24
  .option("--data-file <dataFile>", 'File path from which to read secret data. Set to "-" to read the secret data from stdin.')
23
25
  .action(async (unvalidatedKey, options) => {
24
26
  const projectId = (0, projectUtils_1.needProjectId)(options);
27
+ const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
25
28
  const key = await (0, secrets_1.ensureValidKey)(unvalidatedKey, options);
26
29
  const secret = await (0, secrets_1.ensureSecret)(projectId, key, options);
27
30
  let secretValue;
@@ -41,6 +44,50 @@ exports.default = new command_1.Command("functions:secrets:set <KEY>")
41
44
  }
42
45
  const secretVersion = await (0, secretManager_1.addVersion)(projectId, key, secretValue);
43
46
  (0, utils_1.logSuccess)(`Created a new secret version ${(0, secretManager_1.toSecretVersionResourceName)(secretVersion)}`);
44
- (0, utils_1.logBullet)("Please deploy your functions for the change to take effect by running:\n\t" +
45
- clc.bold("firebase deploy --only functions"));
47
+ if (!secrets.isFirebaseManaged(secret)) {
48
+ (0, utils_1.logBullet)("Please deploy your functions for the change to take effect by running:\n\t" +
49
+ clc.bold("firebase deploy --only functions"));
50
+ return;
51
+ }
52
+ const haveBackend = await backend.existingBackend({ projectId });
53
+ const endpointsToUpdate = backend
54
+ .allEndpoints(haveBackend)
55
+ .filter((e) => secrets.inUse({ projectId, projectNumber }, secret, e));
56
+ if (endpointsToUpdate.length === 0) {
57
+ return;
58
+ }
59
+ (0, utils_1.logBullet)(`${endpointsToUpdate.length} functions are using stale version of secret ${secret.name}:\n\t` +
60
+ endpointsToUpdate.map((e) => `${e.id}(${e.region})`).join("\n\t"));
61
+ if (!options.force) {
62
+ const confirm = await (0, prompt_1.promptOnce)({
63
+ name: "redeploy",
64
+ type: "confirm",
65
+ default: true,
66
+ message: `Do you want to re-deploy the functions and destroy the stale version of secret ${secret.name}?`,
67
+ }, options);
68
+ if (!confirm) {
69
+ (0, utils_1.logBullet)("Please deploy your functions for the change to take effect by running:\n\t" +
70
+ clc.bold("firebase deploy --only functions"));
71
+ return;
72
+ }
73
+ }
74
+ const updateOps = endpointsToUpdate.map(async (e) => {
75
+ (0, utils_1.logBullet)(`Updating function ${e.id}(${e.region})...`);
76
+ const updated = await secrets.updateEndpointSecret({ projectId, projectNumber }, secretVersion, e);
77
+ (0, utils_1.logBullet)(`Updated function ${e.id}(${e.region}).`);
78
+ return updated;
79
+ });
80
+ const updatedEndpoints = await Promise.all(updateOps);
81
+ (0, utils_1.logBullet)(`Pruning stale secrets...`);
82
+ const prunedResult = await (0, secrets_1.pruneAndDestroySecrets)({ projectId, projectNumber }, updatedEndpoints);
83
+ if (prunedResult.destroyed.length > 0) {
84
+ (0, utils_1.logBullet)(`Detroyed unused secret versions: ${prunedResult.destroyed
85
+ .map((s) => `${s.secret}@${s.version}`)
86
+ .join(", ")}`);
87
+ }
88
+ if (prunedResult.erred.length > 0) {
89
+ (0, utils_1.logWarning)(`Failed to destroy unused secret versions:\n\t${prunedResult.erred
90
+ .map((err) => err.message)
91
+ .join("\n\t")}`);
92
+ }
46
93
  });
@@ -6,7 +6,7 @@ const error_1 = require("../error");
6
6
  const api_1 = require("../hosting/api");
7
7
  const normalizedHostingConfigs_1 = require("../hosting/normalizedHostingConfigs");
8
8
  const requirePermissions_1 = require("../requirePermissions");
9
- const deploy = require("../deploy");
9
+ const deploy_1 = require("../deploy");
10
10
  const projectUtils_1 = require("../projectUtils");
11
11
  const logger_1 = require("../logger");
12
12
  const requireConfig_1 = require("../requireConfig");
@@ -82,7 +82,7 @@ exports.default = new command_1.Command("hosting:channel:deploy [channelId]")
82
82
  siteInfo.expireTime = chan.expireTime;
83
83
  return;
84
84
  }));
85
- const { hosting } = await deploy(["hosting"], options, { hostingChannel: channelId });
85
+ const { hosting } = await (0, deploy_1.deploy)(["hosting"], options, { hostingChannel: channelId });
86
86
  const versionNames = [];
87
87
  if (typeof hosting === "string") {
88
88
  versionNames.push(hosting);
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ async function default_1() { }
4
+ exports.default = default_1;
@@ -2,4 +2,5 @@
2
2
  module.exports = {
3
3
  prepare: require("./prepare"),
4
4
  release: require("./release"),
5
+ deploy: require("./deploy").default,
5
6
  };
@@ -10,7 +10,7 @@ const provisioningHelper_1 = require("../../extensions/provisioningHelper");
10
10
  const secrets_1 = require("./secrets");
11
11
  const validate_1 = require("./validate");
12
12
  async function deploy(context, options, payload) {
13
- var _a, _b, _c, _d, _e, _f;
13
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
14
14
  const projectId = (0, projectUtils_1.needProjectId)(options);
15
15
  await (0, validate_1.checkBilling)(projectId, options.nonInteractive);
16
16
  await (0, provisioningHelper_1.bulkCheckProductsProvisioned)(projectId, [
@@ -25,15 +25,15 @@ async function deploy(context, options, payload) {
25
25
  concurrency: 5,
26
26
  handler: tasks.extensionsDeploymentHandler(errorHandler),
27
27
  });
28
- for (const create of (_d = payload.instancesToCreate) !== null && _d !== void 0 ? _d : []) {
28
+ for (const create of (_e = (_d = payload.instancesToCreate) === null || _d === void 0 ? void 0 : _d.filter((i) => !!i.ref)) !== null && _e !== void 0 ? _e : []) {
29
29
  const task = tasks.createExtensionInstanceTask(projectId, create, true);
30
30
  void validationQueue.run(task);
31
31
  }
32
- for (const update of (_e = payload.instancesToUpdate) !== null && _e !== void 0 ? _e : []) {
32
+ for (const update of (_g = (_f = payload.instancesToUpdate) === null || _f === void 0 ? void 0 : _f.filter((i) => !!i.ref)) !== null && _g !== void 0 ? _g : []) {
33
33
  const task = tasks.updateExtensionInstanceTask(projectId, update, true);
34
34
  void validationQueue.run(task);
35
35
  }
36
- for (const configure of (_f = payload.instancesToConfigure) !== null && _f !== void 0 ? _f : []) {
36
+ for (const configure of (_j = (_h = payload.instancesToConfigure) === null || _h === void 0 ? void 0 : _h.filter((i) => !!i.ref)) !== null && _j !== void 0 ? _j : []) {
37
37
  const task = tasks.configureExtensionInstanceTask(projectId, configure, true);
38
38
  void validationQueue.run(task);
39
39
  }
@@ -6,16 +6,19 @@ const refs = require("../../extensions/refs");
6
6
  const humanReadable = (dep) => `${clc.bold(dep.instanceId)} (${dep.ref ? `${refs.toExtensionVersionRef(dep.ref)}` : `Installed from local source`})`;
7
7
  exports.humanReadable = humanReadable;
8
8
  const humanReadableUpdate = (from, to) => {
9
- var _a, _b, _c, _d, _e;
10
- if (((_a = from.ref) === null || _a === void 0 ? void 0 : _a.publisherId) === ((_b = to.ref) === null || _b === void 0 ? void 0 : _b.publisherId) &&
11
- ((_c = from.ref) === null || _c === void 0 ? void 0 : _c.extensionId) === ((_d = to.ref) === null || _d === void 0 ? void 0 : _d.extensionId)) {
12
- return `\t${clc.bold(from.instanceId)} (${refs.toExtensionVersionRef(from.ref)} => ${(_e = to.ref) === null || _e === void 0 ? void 0 : _e.version})`;
9
+ var _a;
10
+ if (from.ref &&
11
+ to.ref &&
12
+ from.ref.publisherId === to.ref.publisherId &&
13
+ from.ref.extensionId === to.ref.extensionId) {
14
+ return `\t${clc.bold(from.instanceId)} (${refs.toExtensionVersionRef(from.ref)} => ${(_a = to.ref) === null || _a === void 0 ? void 0 : _a.version})`;
13
15
  }
14
16
  else {
15
17
  const fromRef = from.ref
16
18
  ? `${refs.toExtensionVersionRef(from.ref)}`
17
19
  : `Installed from local source`;
18
- return `\t${clc.bold(from.instanceId)} (${fromRef} => ${refs.toExtensionVersionRef(to.ref)})`;
20
+ const toRef = to.ref ? `${refs.toExtensionVersionRef(to.ref)}` : `Installed from local source`;
21
+ return `\t${clc.bold(from.instanceId)} (${fromRef} => ${toRef})`;
19
22
  }
20
23
  };
21
24
  function createsSummary(toCreate) {