diff --git a/src/deploy/extensions/args.ts b/src/deploy/extensions/args.ts index 2ff5221e1cf..d7398c4bf56 100644 --- a/src/deploy/extensions/args.ts +++ b/src/deploy/extensions/args.ts @@ -10,4 +10,5 @@ export interface Payload { export interface Context { have?: planner.DeploymentInstanceSpec[]; want?: planner.DeploymentInstanceSpec[]; + extensionsStartTime?: number; } diff --git a/src/deploy/extensions/deploy.ts b/src/deploy/extensions/deploy.ts index 47e1edcbf53..e49d7462f17 100644 --- a/src/deploy/extensions/deploy.ts +++ b/src/deploy/extensions/deploy.ts @@ -59,7 +59,6 @@ export async function deploy(context: Context, options: Options, payload: Payloa validationQueue.process(); validationQueue.close(); - await validationPromise; if (errorHandler.hasErrors()) { diff --git a/src/deploy/extensions/prepare.ts b/src/deploy/extensions/prepare.ts index 403af32073d..47a3b3820ad 100644 --- a/src/deploy/extensions/prepare.ts +++ b/src/deploy/extensions/prepare.ts @@ -17,6 +17,7 @@ import { checkSpecForV2Functions, ensureNecessaryV2ApisAndRoles } from "./v2Func import { acceptLatestAppDeveloperTOS } from "../../extensions/tos"; export async function prepare(context: Context, options: Options, payload: Payload) { + context.extensionsStartTime = Date.now(); const projectId = needProjectId(options); const projectNumber = await needProjectNumber(options); const aliases = getAliases(options, projectId); diff --git a/src/deploy/extensions/release.ts b/src/deploy/extensions/release.ts index a8501a02242..67a5063b0f2 100644 --- a/src/deploy/extensions/release.ts +++ b/src/deploy/extensions/release.ts @@ -7,6 +7,7 @@ import { ErrorHandler } from "./errors"; import { Options } from "../../options"; import { needProjectId } from "../../projectUtils"; import { saveEtags } from "../../extensions/etags"; +import { trackGA4 } from "../../track"; export async function release(context: Context, options: Options, payload: Payload) { const projectId = needProjectId(options); @@ -45,6 +46,20 @@ export async function release(context: Context, options: Options, payload: Paylo deploymentQueue.close(); await deploymentPromise; + // extensionsStartTime should always be populated, but if not, fall back to something that won't break us. + const duration = context.extensionsStartTime ? Date.now() - context.extensionsStartTime : 1; + await trackGA4( + "extensions_deploy", + { + extension_instance_created: payload.instancesToCreate?.length ?? 0, + extension_instance_updated: payload.instancesToUpdate?.length ?? 0, + extension_instance_configured: payload.instancesToConfigure?.length ?? 0, + extension_instance_deleted: payload.instancesToDelete?.length ?? 0, + errors: errorHandler.errors.length ?? 0, + interactive: options.nonInteractive ? "false" : "true", + }, + duration + ); // After deployment, write the latest etags to RC so we can detect out of band changes in the next deploy. const newHave = await planner.have(projectId); diff --git a/src/deploy/hosting/deploy.ts b/src/deploy/hosting/deploy.ts index 2f9fc4e528a..279b250cef7 100644 --- a/src/deploy/hosting/deploy.ts +++ b/src/deploy/hosting/deploy.ts @@ -2,7 +2,6 @@ import { Uploader } from "./uploader"; import { detectProjectRoot } from "../../detectProjectRoot"; import { listFiles } from "../../listFiles"; import { logger } from "../../logger"; -import { track } from "../../track"; import { envOverride, logLabeledBullet, logLabeledSuccess } from "../../utils"; import { bold, cyan } from "colorette"; import * as ora from "ora"; @@ -88,9 +87,6 @@ export async function deploy(context: Context, options: Options): Promise try { await uploader.start(); - } catch (err: any) { - void track("Hosting Deploy", "failure"); - throw err; } finally { clearInterval(progressInterval); updateSpinner(uploader.statusMessage(), debugging); @@ -103,8 +99,6 @@ export async function deploy(context: Context, options: Options): Promise logLabeledSuccess(`hosting[${deploy.config.site}]`, "file upload complete"); const dt = Date.now() - t0; logger.debug(`[hosting] deploy completed after ${dt}ms`); - - void track("Hosting Deploy", "success", dt); return runDeploys(deploys, debugging); } diff --git a/src/emulator/controller.ts b/src/emulator/controller.ts index 7505bf9ea81..1950cb0b346 100644 --- a/src/emulator/controller.ts +++ b/src/emulator/controller.ts @@ -4,7 +4,7 @@ import * as path from "path"; import * as fsConfig from "../firestore/fsConfig"; import { logger } from "../logger"; -import { trackEmulator } from "../track"; +import { trackEmulator, trackGA4 } from "../track"; import * as utils from "../utils"; import { EmulatorRegistry } from "./registry"; import { @@ -351,6 +351,10 @@ export async function startAll( extensionsBackends ); emulatableBackends.push(...filteredExtensionsBackends); + trackGA4("extensions_emulated", { + number_of_extensions_emulated: filteredExtensionsBackends.length, + number_of_extensions_ignored: extensionsBackends.length - filteredExtensionsBackends.length, + }); } const listenConfig = {} as Record; diff --git a/src/track.ts b/src/track.ts index c1aa68eaf33..69a0c92f4bd 100644 --- a/src/track.ts +++ b/src/track.ts @@ -14,7 +14,9 @@ type cliEventNames = | "login" | "api_enabled" | "hosting_version" - | "extension_added_to_manifest"; + | "extension_added_to_manifest" + | "extensions_deploy" + | "extensions_emulated"; type GA4Property = "cli" | "emulator"; interface GA4Info { measurementId: string;