firebase-tools 12.4.0 → 12.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/lib/api/frameworks.js +21 -0
  2. package/lib/api.js +4 -2
  3. package/lib/auth.js +3 -3
  4. package/lib/command.js +15 -4
  5. package/lib/commands/ext-install.js +10 -4
  6. package/lib/commands/index.js +2 -0
  7. package/lib/commands/init.js +8 -0
  8. package/lib/commands/internaltesting-frameworks-init.js +14 -0
  9. package/lib/config.js +1 -0
  10. package/lib/deploy/extensions/prepare.js +1 -0
  11. package/lib/deploy/extensions/release.js +11 -1
  12. package/lib/deploy/functions/checkIam.js +4 -1
  13. package/lib/deploy/functions/prepare.js +2 -1
  14. package/lib/deploy/functions/runtimes/discovery/index.js +6 -0
  15. package/lib/deploy/functions/runtimes/node/index.js +12 -4
  16. package/lib/deploy/functions/runtimes/python/index.js +19 -25
  17. package/lib/deploy/hosting/deploy.js +0 -6
  18. package/lib/deploy/hosting/prepare.js +7 -1
  19. package/lib/deploy/index.js +11 -4
  20. package/lib/deploy/lifecycleHooks.js +3 -0
  21. package/lib/detectProjectRoot.js +4 -1
  22. package/lib/dynamicImport.js +11 -1
  23. package/lib/emulator/commandUtils.js +4 -4
  24. package/lib/emulator/controller.js +9 -7
  25. package/lib/emulator/downloadableEmulators.js +3 -3
  26. package/lib/emulator/functionsEmulator.js +1 -2
  27. package/lib/emulator/storage/index.js +6 -0
  28. package/lib/emulator/storage/rules/manager.js +0 -4
  29. package/lib/emulator/storage/server.js +52 -0
  30. package/lib/ensureApiEnabled.js +3 -1
  31. package/lib/experiments.js +5 -0
  32. package/lib/extensions/paramHelper.js +0 -5
  33. package/lib/frameworks/constants.js +2 -15
  34. package/lib/frameworks/index.js +13 -8
  35. package/lib/frameworks/utils.js +50 -20
  36. package/lib/functionsConfig.js +2 -2
  37. package/lib/gcp/cloudbuild.js +50 -0
  38. package/lib/init/features/composer/repo.js +121 -0
  39. package/lib/init/features/frameworks/constants.js +7 -0
  40. package/lib/init/features/frameworks/index.js +36 -0
  41. package/lib/init/features/index.js +3 -1
  42. package/lib/init/index.js +4 -0
  43. package/lib/management/projects.js +5 -1
  44. package/lib/monospace/index.js +7 -7
  45. package/lib/requireAuth.js +1 -1
  46. package/lib/track.js +91 -52
  47. package/lib/utils.js +6 -1
  48. package/package.json +1 -1
  49. package/schema/extension-yaml.json +432 -0
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createBuild = exports.createStack = exports.API_VERSION = void 0;
4
+ const apiv2_1 = require("../apiv2");
5
+ const api_1 = require("../api");
6
+ exports.API_VERSION = "v1";
7
+ const client = new apiv2_1.Client({
8
+ urlPrefix: api_1.frameworksOrigin,
9
+ auth: true,
10
+ apiVersion: exports.API_VERSION,
11
+ });
12
+ async function createStack(projectId, location, stackId, stack) {
13
+ const res = await client.post(`projects/${projectId}/locations/${location}/stacks`, stack, { queryParams: { stackId } });
14
+ return res.body;
15
+ }
16
+ exports.createStack = createStack;
17
+ async function createBuild(projectId, location, stackId, buildId, build) {
18
+ const res = await client.post(`projects/${projectId}/locations/${location}/stacks/${stackId}/builds`, build, { queryParams: { buildId } });
19
+ return res.body;
20
+ }
21
+ exports.createBuild = createBuild;
package/lib/api.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.githubClientId = exports.secretManagerOrigin = exports.githubApiOrigin = exports.githubOrigin = exports.serviceUsageOrigin = exports.cloudRunApiOrigin = exports.hostingApiOrigin = exports.firebaseStorageOrigin = exports.storageOrigin = exports.runtimeconfigOrigin = exports.rulesOrigin = exports.resourceManagerOrigin = exports.remoteConfigApiOrigin = exports.rtdbMetadataOrigin = exports.rtdbManagementOrigin = exports.realtimeOrigin = exports.extensionsTOSOrigin = exports.extensionsPublisherOrigin = exports.extensionsOrigin = exports.iamOrigin = exports.identityOrigin = exports.hostingOrigin = exports.googleOrigin = exports.pubsubOrigin = exports.cloudTasksOrigin = exports.cloudschedulerOrigin = exports.functionsDefaultRegion = exports.runOrigin = exports.functionsV2Origin = exports.functionsOrigin = exports.firestoreOrigin = exports.firestoreOriginOrEmulator = exports.firedataOrigin = exports.firebaseExtensionsRegistryOrigin = exports.firebaseApiOrigin = exports.eventarcOrigin = exports.dynamicLinksKey = exports.dynamicLinksOrigin = exports.deployOrigin = exports.consoleOrigin = exports.authOrigin = exports.appDistributionOrigin = exports.artifactRegistryDomain = exports.containerRegistryDomain = exports.cloudMonitoringOrigin = exports.cloudloggingOrigin = exports.cloudbillingOrigin = exports.clientSecret = exports.clientId = exports.authProxyOrigin = void 0;
4
- exports.setScopes = exports.getScopes = exports.githubClientSecret = void 0;
3
+ exports.githubApiOrigin = exports.githubOrigin = exports.frameworksOrigin = exports.serviceUsageOrigin = exports.cloudRunApiOrigin = exports.hostingApiOrigin = exports.firebaseStorageOrigin = exports.storageOrigin = exports.runtimeconfigOrigin = exports.rulesOrigin = exports.resourceManagerOrigin = exports.remoteConfigApiOrigin = exports.rtdbMetadataOrigin = exports.rtdbManagementOrigin = exports.realtimeOrigin = exports.extensionsTOSOrigin = exports.extensionsPublisherOrigin = exports.extensionsOrigin = exports.iamOrigin = exports.identityOrigin = exports.hostingOrigin = exports.googleOrigin = exports.pubsubOrigin = exports.cloudTasksOrigin = exports.cloudschedulerOrigin = exports.cloudbuildOrigin = exports.functionsDefaultRegion = exports.runOrigin = exports.functionsV2Origin = exports.functionsOrigin = exports.firestoreOrigin = exports.firestoreOriginOrEmulator = exports.firedataOrigin = exports.firebaseExtensionsRegistryOrigin = exports.firebaseApiOrigin = exports.eventarcOrigin = exports.dynamicLinksKey = exports.dynamicLinksOrigin = exports.deployOrigin = exports.consoleOrigin = exports.authOrigin = exports.appDistributionOrigin = exports.artifactRegistryDomain = exports.containerRegistryDomain = exports.cloudMonitoringOrigin = exports.cloudloggingOrigin = exports.cloudbillingOrigin = exports.clientSecret = exports.clientId = exports.authProxyOrigin = void 0;
4
+ exports.setScopes = exports.getScopes = exports.githubClientSecret = exports.githubClientId = exports.secretManagerOrigin = void 0;
5
5
  const constants_1 = require("./emulator/constants");
6
6
  const logger_1 = require("./logger");
7
7
  const scopes = require("./scopes");
@@ -36,6 +36,7 @@ exports.functionsOrigin = utils.envOverride("FIREBASE_FUNCTIONS_URL", "https://c
36
36
  exports.functionsV2Origin = utils.envOverride("FIREBASE_FUNCTIONS_V2_URL", "https://cloudfunctions.googleapis.com");
37
37
  exports.runOrigin = utils.envOverride("CLOUD_RUN_URL", "https://run.googleapis.com");
38
38
  exports.functionsDefaultRegion = utils.envOverride("FIREBASE_FUNCTIONS_DEFAULT_REGION", "us-central1");
39
+ exports.cloudbuildOrigin = utils.envOverride("FIREBASE_CLOUDBUILD_URL", "https://cloudbuild.googleapis.com");
39
40
  exports.cloudschedulerOrigin = utils.envOverride("FIREBASE_CLOUDSCHEDULER_URL", "https://cloudscheduler.googleapis.com");
40
41
  exports.cloudTasksOrigin = utils.envOverride("FIREBASE_CLOUD_TAKS_URL", "https://cloudtasks.googleapis.com");
41
42
  exports.pubsubOrigin = utils.envOverride("FIREBASE_PUBSUB_URL", "https://pubsub.googleapis.com");
@@ -58,6 +59,7 @@ exports.firebaseStorageOrigin = utils.envOverride("FIREBASE_FIREBASESTORAGE_URL"
58
59
  exports.hostingApiOrigin = utils.envOverride("FIREBASE_HOSTING_API_URL", "https://firebasehosting.googleapis.com");
59
60
  exports.cloudRunApiOrigin = utils.envOverride("CLOUD_RUN_API_URL", "https://run.googleapis.com");
60
61
  exports.serviceUsageOrigin = utils.envOverride("FIREBASE_SERVICE_USAGE_URL", "https://serviceusage.googleapis.com");
62
+ exports.frameworksOrigin = utils.envOverride("FRAMEWORKS_URL", "https://placeholder.googleapis.com");
61
63
  exports.githubOrigin = utils.envOverride("GITHUB_URL", "https://github.com");
62
64
  exports.githubApiOrigin = utils.envOverride("GITHUB_API_URL", "https://api.github.com");
63
65
  exports.secretManagerOrigin = utils.envOverride("CLOUD_SECRET_MANAGER_URL", "https://secretmanager.googleapis.com");
package/lib/auth.js CHANGED
@@ -307,7 +307,7 @@ async function loginRemotely() {
307
307
  });
308
308
  try {
309
309
  const tokens = await getTokensFromAuthorizationCode(code, `${api_1.authProxyOrigin}/complete`, codeVerifier);
310
- void (0, track_1.track)("login", "google_remote");
310
+ void (0, track_1.trackGA4)("login", { method: "google_remote" });
311
311
  return {
312
312
  user: jwt.decode(tokens.id_token),
313
313
  tokens: tokens,
@@ -323,7 +323,7 @@ async function loginWithLocalhostGoogle(port, userHint) {
323
323
  const authUrl = getLoginUrl(callbackUrl, userHint);
324
324
  const successTemplate = "../templates/loginSuccess.html";
325
325
  const tokens = await loginWithLocalhost(port, callbackUrl, authUrl, successTemplate, getTokensFromAuthorizationCode);
326
- void (0, track_1.track)("login", "google_localhost");
326
+ void (0, track_1.trackGA4)("login", { method: "google_localhost" });
327
327
  return {
328
328
  user: jwt.decode(tokens.id_token),
329
329
  tokens: tokens,
@@ -335,7 +335,7 @@ async function loginWithLocalhostGitHub(port) {
335
335
  const authUrl = getGithubLoginUrl(callbackUrl);
336
336
  const successTemplate = "../templates/loginSuccessGithub.html";
337
337
  const tokens = await loginWithLocalhost(port, callbackUrl, authUrl, successTemplate, getGithubTokensFromAuthorizationCode);
338
- void (0, track_1.track)("login", "google_localhost");
338
+ void (0, track_1.trackGA4)("login", { method: "github_localhost" });
339
339
  return tokens;
340
340
  }
341
341
  async function loginWithLocalhost(port, callbackUrl, authUrl, successTemplate, getTokens) {
package/lib/command.js CHANGED
@@ -89,7 +89,12 @@ class Command {
89
89
  }, null, 2));
90
90
  }
91
91
  const duration = Math.floor((process.uptime() - start) * 1000);
92
- const trackSuccess = (0, track_1.track)(this.name, "success", duration);
92
+ const trackSuccess = (0, track_1.trackGA4)("command_execution", {
93
+ command_name: this.name,
94
+ result: "success",
95
+ duration,
96
+ interactive: (0, utils_1.getInheritedOption)(options, "nonInteractive") ? "false" : "true",
97
+ });
93
98
  if (!isEmulator) {
94
99
  await (0, utils_1.withTimeout)(5000, trackSuccess);
95
100
  }
@@ -113,8 +118,11 @@ class Command {
113
118
  }
114
119
  const duration = Math.floor((process.uptime() - start) * 1000);
115
120
  await (0, utils_1.withTimeout)(5000, Promise.all([
116
- (0, track_1.track)(this.name, "error", duration),
117
- (0, track_1.track)(err.exit === 1 ? "Error (User)" : "Error (Unexpected)", "", duration),
121
+ (0, track_1.trackGA4)("command_execution", {
122
+ command_name: this.name,
123
+ result: "error",
124
+ interactive: (0, utils_1.getInheritedOption)(options, "nonInteractive") ? "false" : "true",
125
+ }, duration),
118
126
  isEmulator
119
127
  ? (0, track_1.trackEmulator)("command_error", {
120
128
  command_name: this.name,
@@ -223,7 +231,10 @@ function validateProjectId(project) {
223
231
  if (PROJECT_ID_REGEX.test(project)) {
224
232
  return;
225
233
  }
226
- (0, track_1.track)("Project ID Check", "invalid");
234
+ (0, track_1.trackGA4)("error", {
235
+ error_type: "Error (User)",
236
+ details: "Invalid project ID",
237
+ });
227
238
  const invalidMessage = "Invalid project id: " + clc.bold(project) + ".";
228
239
  if (project.toLowerCase() !== project) {
229
240
  throw new error_1.FirebaseError(invalidMessage + "\nNote: Project id must be all lowercase.");
@@ -35,7 +35,7 @@ exports.command = new command_1.Command("ext:install [extensionName]")
35
35
  .before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
36
36
  .before(extensionsHelper_1.diagnoseAndFixProject)
37
37
  .action(async (extensionName, options) => {
38
- var _a;
38
+ var _a, _b;
39
39
  const projectId = (0, projectUtils_1.getProjectId)(options);
40
40
  const paramsEnvPath = "";
41
41
  let learnMore = false;
@@ -61,12 +61,18 @@ exports.command = new command_1.Command("ext:install [extensionName]")
61
61
  if ((0, extensionsHelper_1.isLocalPath)(extensionName)) {
62
62
  source = await (0, extensionsHelper_1.createSourceFromLocation)((0, projectUtils_1.needProjectId)({ projectId }), extensionName);
63
63
  await (0, displayExtensionInfo_1.displayExtInfo)(extensionName, "", source.spec);
64
- void (0, track_1.track)("Extension Install", "Install by Source", options.interactive ? 1 : 0);
64
+ void (0, track_1.trackGA4)("extension_added_to_manifest", {
65
+ published: "local",
66
+ interactive: options.nonInteractive ? "false" : "true",
67
+ });
65
68
  }
66
69
  else {
67
- void (0, track_1.track)("Extension Install", "Install by Extension Ref", options.interactive ? 1 : 0);
68
70
  extensionName = await (0, extensionsHelper_1.canonicalizeRefInput)(extensionName);
69
71
  extensionVersion = await extensionsApi.getExtensionVersion(extensionName);
72
+ void (0, track_1.trackGA4)("extension_added_to_manifest", {
73
+ published: ((_a = extensionVersion.listing) === null || _a === void 0 ? void 0 : _a.state) === "APPROVED" ? "published" : "uploaded",
74
+ interactive: options.nonInteractive ? "false" : "true",
75
+ });
70
76
  await infoExtensionVersion({
71
77
  extensionName,
72
78
  extensionVersion,
@@ -82,7 +88,7 @@ exports.command = new command_1.Command("ext:install [extensionName]")
82
88
  if (!source && !extensionVersion) {
83
89
  throw new error_1.FirebaseError("Could not find a source. Please specify a valid source to continue.");
84
90
  }
85
- const spec = (_a = source === null || source === void 0 ? void 0 : source.spec) !== null && _a !== void 0 ? _a : extensionVersion === null || extensionVersion === void 0 ? void 0 : extensionVersion.spec;
91
+ 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;
86
92
  if (!spec) {
87
93
  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.`);
88
94
  }
@@ -144,6 +144,8 @@ function load(client) {
144
144
  client.internaltesting.frameworks.compose = loadCommand("internaltesting-frameworks-compose");
145
145
  client.internaltesting.functions = {};
146
146
  client.internaltesting.functions.discover = loadCommand("internaltesting-functions-discover");
147
+ client.internaltesting.frameworks = {};
148
+ client.internaltesting.frameworks.init = loadCommand("internaltesting-frameworks-init");
147
149
  }
148
150
  client.login = loadCommand("login");
149
151
  client.login.add = loadCommand("login-add");
@@ -14,6 +14,7 @@ const prompt_1 = require("../prompt");
14
14
  const requireAuth_1 = require("../requireAuth");
15
15
  const fsutils = require("../fsutils");
16
16
  const utils = require("../utils");
17
+ const experiments_1 = require("../experiments");
17
18
  const homeDir = os.homedir();
18
19
  const TEMPLATE_ROOT = path.resolve(__dirname, "../../templates/");
19
20
  const BANNER_TEXT = fs.readFileSync(path.join(TEMPLATE_ROOT, "banner.txt"), "utf8");
@@ -68,6 +69,13 @@ const choices = [
68
69
  checked: false,
69
70
  },
70
71
  ];
72
+ if ((0, experiments_1.isEnabled)("frameworks")) {
73
+ choices.push({
74
+ value: "frameworks",
75
+ name: "Frameworks: Get started with Frameworks projects.",
76
+ checked: false,
77
+ });
78
+ }
71
79
  const featureNames = choices.map((choice) => choice.value);
72
80
  const DESCRIPTION = `Interactively configure the current directory as a Firebase project or initialize new features in an already configured Firebase project directory.
73
81
 
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const repo_1 = require("../init/features/composer/repo");
6
+ const projectUtils_1 = require("../projectUtils");
7
+ const requireInteractive_1 = require("../requireInteractive");
8
+ exports.command = new command_1.Command("internaltesting:frameworks:init")
9
+ .description("connect github repo to cloud build")
10
+ .before(requireInteractive_1.default)
11
+ .action(async (options) => {
12
+ const projectId = (0, projectUtils_1.needProjectId)(options);
13
+ await (0, repo_1.linkGitHubRepository)(projectId, "us-central2", "stack0");
14
+ });
package/lib/config.js CHANGED
@@ -208,6 +208,7 @@ class Config {
208
208
  }
209
209
  throw new error_1.FirebaseError("Not in a Firebase app directory (could not locate firebase.json)", {
210
210
  exit: 1,
211
+ status: 404,
211
212
  });
212
213
  }
213
214
  }
@@ -18,6 +18,7 @@ const v2FunctionHelper_1 = require("./v2FunctionHelper");
18
18
  const tos_1 = require("../../extensions/tos");
19
19
  async function prepare(context, options, payload) {
20
20
  var _a, _b;
21
+ context.extensionsStartTime = Date.now();
21
22
  const projectId = (0, projectUtils_1.needProjectId)(options);
22
23
  const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
23
24
  const aliases = (0, projectUtils_1.getAliases)(options, projectId);
@@ -8,8 +8,9 @@ const error_1 = require("../../error");
8
8
  const errors_1 = require("./errors");
9
9
  const projectUtils_1 = require("../../projectUtils");
10
10
  const etags_1 = require("../../extensions/etags");
11
+ const track_1 = require("../../track");
11
12
  async function release(context, options, payload) {
12
- var _a, _b, _c, _d;
13
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
13
14
  const projectId = (0, projectUtils_1.needProjectId)(options);
14
15
  const errorHandler = new errors_1.ErrorHandler();
15
16
  const deploymentQueue = new queue_1.default({
@@ -37,6 +38,15 @@ async function release(context, options, payload) {
37
38
  deploymentQueue.process();
38
39
  deploymentQueue.close();
39
40
  await deploymentPromise;
41
+ const duration = context.extensionsStartTime ? Date.now() - context.extensionsStartTime : 1;
42
+ await (0, track_1.trackGA4)("extensions_deploy", {
43
+ extension_instance_created: (_f = (_e = payload.instancesToCreate) === null || _e === void 0 ? void 0 : _e.length) !== null && _f !== void 0 ? _f : 0,
44
+ extension_instance_updated: (_h = (_g = payload.instancesToUpdate) === null || _g === void 0 ? void 0 : _g.length) !== null && _h !== void 0 ? _h : 0,
45
+ extension_instance_configured: (_k = (_j = payload.instancesToConfigure) === null || _j === void 0 ? void 0 : _j.length) !== null && _k !== void 0 ? _k : 0,
46
+ extension_instance_deleted: (_m = (_l = payload.instancesToDelete) === null || _l === void 0 ? void 0 : _l.length) !== null && _m !== void 0 ? _m : 0,
47
+ errors: (_o = errorHandler.errors.length) !== null && _o !== void 0 ? _o : 0,
48
+ interactive: options.nonInteractive ? "false" : "true",
49
+ }, duration);
40
50
  const newHave = await planner.have(projectId);
41
51
  (0, etags_1.saveEtags)(options.rc, projectId, newHave);
42
52
  if (errorHandler.hasErrors()) {
@@ -59,7 +59,10 @@ async function checkHttpIam(context, options, payload) {
59
59
  return;
60
60
  }
61
61
  if (!passed) {
62
- void (0, track_1.track)("Error (User)", "deploy:functions:http_create_missing_iam");
62
+ void (0, track_1.trackGA4)("error", {
63
+ error_type: "Error (User)",
64
+ details: "deploy:functions:http_create_missing_iam",
65
+ });
63
66
  throw new error_1.FirebaseError(`Missing required permission on project ${(0, colorette_1.bold)(context.projectId)} to deploy new HTTPS functions. The permission ${(0, colorette_1.bold)(PERMISSION)} is required to deploy the following functions:\n\n- ` +
64
67
  newHttpsEndpoints.map((func) => func.id).join("\n- ") +
65
68
  `\n\nTo address this error, please ask a project Owner to assign your account the "Cloud Functions Admin" role at the following URL:\n\nhttps://console.cloud.google.com/iam-admin/iam?project=${context.projectId}`);
@@ -301,7 +301,8 @@ async function loadCodebases(config, options, firebaseConfig, runtimeConfig, fil
301
301
  logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
302
302
  await runtimeDelegate.build();
303
303
  const firebaseEnvs = functionsEnv.loadFirebaseEnvs(firebaseConfig, projectId);
304
- wantBuilds[codebase] = await runtimeDelegate.discoverBuild(runtimeConfig, firebaseEnvs);
304
+ (0, utils_1.logLabeledBullet)("functions", `Loading and anaylzing source code for codebase ${codebase} to determine what to deploy`);
305
+ wantBuilds[codebase] = await runtimeDelegate.discoverBuild(runtimeConfig, Object.assign(Object.assign({}, firebaseEnvs), { GOOGLE_CLOUD_QUOTA_PROJECT: projectId }));
305
306
  }
306
307
  return wantBuilds;
307
308
  }
@@ -64,6 +64,12 @@ async function detectFromPort(port, project, runtime, timeout = 10000) {
64
64
  throw err;
65
65
  }
66
66
  }
67
+ if (res.status !== 200) {
68
+ const text = await res.text();
69
+ logger_1.logger.debug(`Got response code ${res.status}; body ${text}`);
70
+ throw new error_1.FirebaseError("Functions codebase could not be analyzed successfully. " +
71
+ "It may have a syntax or runtime error");
72
+ }
67
73
  const text = await res.text();
68
74
  logger_1.logger.debug("Got response from /__/functions.yaml", text);
69
75
  let parsed;
@@ -92,7 +92,7 @@ class Delegate {
92
92
  return Promise.resolve(() => Promise.resolve());
93
93
  }
94
94
  serveAdmin(port, config, envs) {
95
- var _a;
95
+ var _a, _b;
96
96
  const env = Object.assign(Object.assign({}, envs), { PORT: port, FUNCTIONS_CONTROL_API: "true", HOME: process.env.HOME, PATH: process.env.PATH, NODE_ENV: process.env.NODE_ENV, __FIREBASE_FRAMEWORKS_ENTRY__: process.env.__FIREBASE_FRAMEWORKS_ENTRY__ });
97
97
  if (Object.keys(config || {}).length) {
98
98
  env.CLOUD_RUNTIME_CONFIG = JSON.stringify(config);
@@ -114,17 +114,25 @@ class Delegate {
114
114
  const childProcess = spawn(binPath, [this.sourceDir], {
115
115
  env,
116
116
  cwd: this.sourceDir,
117
- stdio: ["ignore", "pipe", "inherit"],
117
+ stdio: ["ignore", "pipe", "pipe"],
118
118
  });
119
119
  (_a = childProcess.stdout) === null || _a === void 0 ? void 0 : _a.on("data", (chunk) => {
120
- logger_1.logger.debug(chunk.toString());
120
+ logger_1.logger.info(chunk.toString("utf8"));
121
+ });
122
+ (_b = childProcess.stderr) === null || _b === void 0 ? void 0 : _b.on("data", (chunk) => {
123
+ logger_1.logger.error(chunk.toString("utf8"));
121
124
  });
122
125
  return Promise.resolve(async () => {
123
126
  const p = new Promise((resolve, reject) => {
124
127
  childProcess.once("exit", resolve);
125
128
  childProcess.once("error", reject);
126
129
  });
127
- await (0, node_fetch_1.default)(`http://localhost:${port}/__/quitquitquit`);
130
+ try {
131
+ await (0, node_fetch_1.default)(`http://localhost:${port}/__/quitquitquit`);
132
+ }
133
+ catch (e) {
134
+ logger_1.logger.debug("Failed to call quitquitquit. This often means the server failed to start", e);
135
+ }
128
136
  setTimeout(() => {
129
137
  if (!childProcess.killed) {
130
138
  childProcess.kill("SIGKILL");
@@ -11,7 +11,6 @@ const discovery = require("../discovery");
11
11
  const logger_1 = require("../../../../logger");
12
12
  const python_1 = require("../../../../functions/python");
13
13
  const error_1 = require("../../../../error");
14
- const utils_1 = require("../../../../utils");
15
14
  exports.LATEST_VERSION = "python311";
16
15
  async function tryCreateDelegate(context) {
17
16
  const requirementsTextPath = path.join(context.sourceDir, "requirements.txt");
@@ -108,32 +107,31 @@ class Delegate {
108
107
  const modulesDir = await this.modulesDir();
109
108
  const envWithAdminPort = Object.assign(Object.assign({}, envs), { ADMIN_PORT: port.toString() });
110
109
  const args = [this.bin, `"${path.join(modulesDir, "private", "serving.py")}"`];
111
- const stdout = [];
112
- const stderr = [];
113
110
  logger_1.logger.debug(`Running admin server with args: ${JSON.stringify(args)} and env: ${JSON.stringify(envWithAdminPort)} in ${this.sourceDir}`);
114
111
  const childProcess = (0, python_1.runWithVirtualEnv)(args, this.sourceDir, envWithAdminPort);
115
112
  (_a = childProcess.stdout) === null || _a === void 0 ? void 0 : _a.on("data", (chunk) => {
116
- const chunkString = chunk.toString();
117
- stdout.push(chunkString);
118
- logger_1.logger.debug(`stdout: ${chunkString}`);
113
+ logger_1.logger.info(chunk.toString("utf8"));
119
114
  });
120
115
  (_b = childProcess.stderr) === null || _b === void 0 ? void 0 : _b.on("data", (chunk) => {
121
- const chunkString = chunk.toString();
122
- stderr.push(chunkString);
123
- logger_1.logger.debug(`stderr: ${chunkString}`);
116
+ logger_1.logger.error(chunk.toString("utf8"));
124
117
  });
125
- return Promise.resolve({
126
- stderr,
127
- stdout,
128
- killProcess: async () => {
118
+ return Promise.resolve(async () => {
119
+ try {
129
120
  await (0, node_fetch_1.default)(`http://127.0.0.1:${port}/__/quitquitquit`);
130
- const quitTimeout = setTimeout(() => {
131
- if (!childProcess.killed) {
132
- childProcess.kill("SIGKILL");
133
- }
134
- }, 10000);
135
- clearTimeout(quitTimeout);
136
- },
121
+ }
122
+ catch (e) {
123
+ logger_1.logger.debug("Failed to call quitquitquit. This often means the server failed to start", e);
124
+ }
125
+ const quitTimeout = setTimeout(() => {
126
+ if (!childProcess.killed) {
127
+ childProcess.kill("SIGKILL");
128
+ }
129
+ }, 10000);
130
+ clearTimeout(quitTimeout);
131
+ return new Promise((resolve, reject) => {
132
+ childProcess.once("exit", resolve);
133
+ childProcess.once("error", reject);
134
+ });
137
135
  });
138
136
  }
139
137
  async discoverBuild(_configValues, envs) {
@@ -142,14 +140,10 @@ class Delegate {
142
140
  const adminPort = await portfinder.getPortPromise({
143
141
  port: 8081,
144
142
  });
145
- const { killProcess, stderr } = await this.serveAdmin(adminPort, envs);
143
+ const killProcess = await this.serveAdmin(adminPort, envs);
146
144
  try {
147
145
  discovered = await discovery.detectFromPort(adminPort, this.projectId, this.runtime);
148
146
  }
149
- catch (e) {
150
- (0, utils_1.logLabeledWarning)("functions", `Failed to detect functions from source ${e}.\nstderr:${stderr.join("\n")}`);
151
- throw e;
152
- }
153
147
  finally {
154
148
  await killProcess();
155
149
  }
@@ -5,7 +5,6 @@ const uploader_1 = require("./uploader");
5
5
  const detectProjectRoot_1 = require("../../detectProjectRoot");
6
6
  const listFiles_1 = require("../../listFiles");
7
7
  const logger_1 = require("../../logger");
8
- const track_1 = require("../../track");
9
8
  const utils_1 = require("../../utils");
10
9
  const colorette_1 = require("colorette");
11
10
  const ora = require("ora");
@@ -67,10 +66,6 @@ async function deploy(context, options) {
67
66
  try {
68
67
  await uploader.start();
69
68
  }
70
- catch (err) {
71
- void (0, track_1.track)("Hosting Deploy", "failure");
72
- throw err;
73
- }
74
69
  finally {
75
70
  clearInterval(progressInterval);
76
71
  updateSpinner(uploader.statusMessage(), debugging);
@@ -81,7 +76,6 @@ async function deploy(context, options) {
81
76
  (0, utils_1.logLabeledSuccess)(`hosting[${deploy.config.site}]`, "file upload complete");
82
77
  const dt = Date.now() - t0;
83
78
  logger_1.logger.debug(`[hosting] deploy completed after ${dt}ms`);
84
- void (0, track_1.track)("Hosting Deploy", "success", dt);
85
79
  return runDeploys(deploys, debugging);
86
80
  }
87
81
  const debugging = !!(options.debug || options.nonInteractive);
@@ -11,6 +11,7 @@ const track_1 = require("../../track");
11
11
  const utils = require("../../utils");
12
12
  const backend = require("../functions/backend");
13
13
  const ensureTargeted_1 = require("../../functions/ensureTargeted");
14
+ const frameworks_1 = require("../../frameworks");
14
15
  function handlePublicDirectoryFlag(options) {
15
16
  if (options.public) {
16
17
  if (Array.isArray(options.config.get("hosting"))) {
@@ -48,6 +49,9 @@ async function addPinnedFunctionsToOnlyString(context, options) {
48
49
  if (endpoint) {
49
50
  options.only = (0, ensureTargeted_1.ensureTargeted)(options.only, endpoint.codebase || "default", endpoint.id);
50
51
  }
52
+ else if (c.webFramework) {
53
+ options.only = (0, ensureTargeted_1.ensureTargeted)(options.only, (0, frameworks_1.generateSSRCodebaseId)(c.site), r.function.functionId);
54
+ }
51
55
  else {
52
56
  options.only = (0, ensureTargeted_1.ensureTargeted)(options.only, r.function.functionId);
53
57
  }
@@ -94,7 +98,9 @@ async function prepare(context, options) {
94
98
  labels,
95
99
  };
96
100
  const [, versionName] = await Promise.all([
97
- (0, track_1.track)("hosting_deploy", config.webFramework || "classic"),
101
+ (0, track_1.trackGA4)("hosting_version", {
102
+ framework: config.webFramework || "classic",
103
+ }),
98
104
  api.createVersion(config.site, version),
99
105
  ]);
100
106
  return versionName;
@@ -96,11 +96,18 @@ const deploy = async function (targetNames, options, customContext = {}) {
96
96
  await chain(deploys, context, options, payload);
97
97
  await chain(releases, context, options, payload);
98
98
  await chain(postdeploys, context, options, payload);
99
- if ((0, lodash_1.has)(options, "config.notes.databaseRules")) {
100
- await (0, track_1.track)("Rules Deploy", options.config.notes.databaseRules);
101
- }
102
99
  const duration = Date.now() - startTime;
103
- await (0, track_1.track)("Product Deploy", [...targetNames].sort().join(","), duration);
100
+ const analyticsParams = {
101
+ interactive: options.nonInteractive ? "false" : "true",
102
+ };
103
+ Object.keys(TARGETS).reduce((accum, t) => {
104
+ accum[t] = "false";
105
+ return accum;
106
+ }, analyticsParams);
107
+ for (const t of targetNames) {
108
+ analyticsParams[t] = "true";
109
+ }
110
+ await (0, track_1.trackGA4)("product_deploy", analyticsParams, duration);
104
111
  logger_1.logger.info();
105
112
  (0, utils_1.logSuccess)((0, colorette_1.bold)((0, colorette_1.underline)("Deploy complete!")));
106
113
  logger_1.logger.info();
@@ -115,6 +115,9 @@ function getReleventConfigs(target, options) {
115
115
  return individualOnly.replace(`${target}:`, "");
116
116
  });
117
117
  return targetConfigs.filter((config) => {
118
+ if (target === "functions") {
119
+ return onlyTargets.includes(config.codebase);
120
+ }
118
121
  return !config.target || onlyTargets.includes(config.target);
119
122
  });
120
123
  }
@@ -9,7 +9,10 @@ function detectProjectRoot(options) {
9
9
  if (options.configPath) {
10
10
  const fullPath = (0, path_1.resolve)(projectRootDir, options.configPath);
11
11
  if (!(0, fsutils_1.fileExistsSync)(fullPath)) {
12
- throw new error_1.FirebaseError(`Could not load config file ${options.configPath}.`, { exit: 1 });
12
+ throw new error_1.FirebaseError(`Could not load config file ${options.configPath}.`, {
13
+ exit: 1,
14
+ status: 404,
15
+ });
13
16
  }
14
17
  return (0, path_1.dirname)(fullPath);
15
18
  }
@@ -1,10 +1,20 @@
1
1
  const { pathToFileURL } = require("url");
2
2
 
3
+ // If being compiled with webpack, use non webpack require for these calls.
4
+ // (VSCode plugin uses webpack which by default replaces require calls
5
+ // with its own require, which doesn't work on files)
6
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
7
+ const requireFunc =
8
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
9
+ // @ts-ignore prevent VSCE webpack from erroring on non_webpack_require
10
+ // eslint-disable-next-line camelcase
11
+ typeof __webpack_require__ === "function" ? __non_webpack_require__ : require;
12
+
3
13
  exports.dynamicImport = function(mod) {
4
14
  if (mod.startsWith("file://")) return import(mod);
5
15
  if (mod.startsWith("/")) return import(pathToFileURL(mod).toString());
6
16
  try {
7
- const path = require.resolve(mod);
17
+ const path = requireFunc.resolve(mod);
8
18
  return import(pathToFileURL(path).toString());
9
19
  } catch(e) {
10
20
  return Promise.reject(e);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.JAVA_DEPRECATION_WARNING = exports.MIN_SUPPORTED_JAVA_MAJOR_VERSION = exports.checkJavaMajorVersion = exports.emulatorExec = exports.getListenOverview = exports.shutdownWhenKilled = exports.setExportOnExitOptions = exports.parseInspectionPort = exports.beforeEmulatorCommand = exports.warnEmulatorNotSupported = exports.printNoticeIfEmulated = exports.DESC_TEST_PARAMS = exports.FLAG_TEST_PARAMS = exports.DESC_TEST_CONFIG = exports.FLAG_TEST_CONFIG = exports.DESC_UI = exports.FLAG_UI = exports.EXPORT_ON_EXIT_CWD_DANGER = exports.EXPORT_ON_EXIT_USAGE_ERROR = exports.DESC_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT_NAME = exports.DESC_IMPORT = exports.FLAG_IMPORT = exports.DESC_INSPECT_FUNCTIONS = exports.FLAG_INSPECT_FUNCTIONS = exports.DESC_ONLY = exports.FLAG_ONLY = void 0;
3
+ exports.JAVA_DEPRECATION_WARNING = exports.MIN_SUPPORTED_JAVA_MAJOR_VERSION = exports.checkJavaMajorVersion = exports.emulatorExec = exports.getListenOverview = exports.shutdownWhenKilled = exports.setExportOnExitOptions = exports.parseInspectionPort = exports.beforeEmulatorCommand = exports.warnEmulatorNotSupported = exports.printNoticeIfEmulated = exports.DEFAULT_CONFIG = exports.DESC_TEST_PARAMS = exports.FLAG_TEST_PARAMS = exports.DESC_TEST_CONFIG = exports.FLAG_TEST_CONFIG = exports.DESC_UI = exports.FLAG_UI = exports.EXPORT_ON_EXIT_CWD_DANGER = exports.EXPORT_ON_EXIT_USAGE_ERROR = exports.DESC_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT_NAME = exports.DESC_IMPORT = exports.FLAG_IMPORT = exports.DESC_INSPECT_FUNCTIONS = exports.FLAG_INSPECT_FUNCTIONS = exports.DESC_ONLY = exports.FLAG_ONLY = void 0;
4
4
  const clc = require("colorette");
5
5
  const childProcess = require("child_process");
6
6
  const controller = require("../emulator/controller");
@@ -43,7 +43,7 @@ exports.FLAG_TEST_CONFIG = "--test-config <firebase.json file>";
43
43
  exports.DESC_TEST_CONFIG = "A firebase.json style file. Used to configure the Firestore and Realtime Database emulators.";
44
44
  exports.FLAG_TEST_PARAMS = "--test-params <params.env file>";
45
45
  exports.DESC_TEST_PARAMS = "A .env file containing test param values for your emulated extension.";
46
- const DEFAULT_CONFIG = new config_1.Config({
46
+ exports.DEFAULT_CONFIG = new config_1.Config({
47
47
  eventarc: {},
48
48
  database: {},
49
49
  firestore: {},
@@ -92,7 +92,7 @@ function warnEmulatorNotSupported(options, emulator) {
92
92
  }
93
93
  exports.warnEmulatorNotSupported = warnEmulatorNotSupported;
94
94
  async function beforeEmulatorCommand(options) {
95
- const optionsWithDefaultConfig = Object.assign(Object.assign({}, options), { config: DEFAULT_CONFIG });
95
+ const optionsWithDefaultConfig = Object.assign(Object.assign({}, options), { config: exports.DEFAULT_CONFIG });
96
96
  const optionsWithConfig = options.config ? options : optionsWithDefaultConfig;
97
97
  const canStartWithoutConfig = options.only &&
98
98
  !controller.shouldStart(optionsWithConfig, types_1.Emulators.FUNCTIONS) &&
@@ -106,7 +106,7 @@ async function beforeEmulatorCommand(options) {
106
106
  }
107
107
  if (canStartWithoutConfig && !options.config) {
108
108
  utils.logWarning("Could not find config (firebase.json) so using defaults.");
109
- options.config = DEFAULT_CONFIG;
109
+ options.config = exports.DEFAULT_CONFIG;
110
110
  }
111
111
  else {
112
112
  await (0, requireConfig_1.requireConfig)(options);
@@ -209,6 +209,10 @@ async function startAll(options, showUI = true, runningTestScript = false) {
209
209
  const extensionsBackends = await extensionEmulator.getExtensionBackends();
210
210
  const filteredExtensionsBackends = extensionEmulator.filterUnemulatedTriggers(options, extensionsBackends);
211
211
  emulatableBackends.push(...filteredExtensionsBackends);
212
+ (0, track_1.trackGA4)("extensions_emulated", {
213
+ number_of_extensions_emulated: filteredExtensionsBackends.length,
214
+ number_of_extensions_ignored: extensionsBackends.length - filteredExtensionsBackends.length,
215
+ });
212
216
  }
213
217
  const listenConfig = {};
214
218
  if (emulatableBackends.length) {
@@ -248,7 +252,6 @@ async function startAll(options, showUI = true, runningTestScript = false) {
248
252
  }
249
253
  function startEmulator(instance) {
250
254
  const name = instance.getName();
251
- void (0, track_1.track)("Emulator Run", name);
252
255
  void (0, track_1.trackEmulator)("emulator_run", {
253
256
  emulator_name: name,
254
257
  is_demo_project: String(isDemoProject),
@@ -261,7 +264,6 @@ async function startAll(options, showUI = true, runningTestScript = false) {
261
264
  listen: listenForEmulator[types_1.Emulators.HUB],
262
265
  listenForEmulator,
263
266
  });
264
- void (0, track_1.track)("emulators:start", "hub");
265
267
  await startEmulator(hub);
266
268
  }
267
269
  let exportMetadata = {
@@ -531,11 +533,6 @@ async function startAll(options, showUI = true, runningTestScript = false) {
531
533
  });
532
534
  await startEmulator(hostingEmulator);
533
535
  }
534
- if (showUI && !shouldStart(options, types_1.Emulators.UI)) {
535
- hubLogger.logLabeled("WARN", "emulators", "The Emulator UI is not starting, either because none of the emulated " +
536
- "products have an interaction layer in Emulator UI or it cannot " +
537
- "determine the Project ID. Pass the --project flag to specify a project.");
538
- }
539
536
  if (listenForEmulator.logging) {
540
537
  const loggingAddr = legacyGetFirstAddr(types_1.Emulators.LOGGING);
541
538
  const loggingEmulator = new loggingEmulator_1.LoggingEmulator({
@@ -544,6 +541,11 @@ async function startAll(options, showUI = true, runningTestScript = false) {
544
541
  });
545
542
  await startEmulator(loggingEmulator);
546
543
  }
544
+ if (showUI && !shouldStart(options, types_1.Emulators.UI)) {
545
+ hubLogger.logLabeled("WARN", "emulators", "The Emulator UI is not starting, either because none of the running " +
546
+ "emulators have a UI component or the Emulator UI cannot " +
547
+ "determine the Project ID. Pass the --project flag to specify a project.");
548
+ }
547
549
  if (listenForEmulator.ui) {
548
550
  const ui = new ui_1.EmulatorUI({
549
551
  projectId: projectId,