From ad6fd339a4d8d2c5c8e2be04b3150fcf4b75b457 Mon Sep 17 00:00:00 2001 From: Brian Li Date: Wed, 28 Jun 2023 12:37:57 -0400 Subject: [PATCH 1/4] track functions metrics with ga4 --- src/deploy/functions/args.ts | 14 +++ src/deploy/functions/build.ts | 2 + src/deploy/functions/prepare.ts | 14 +++ src/deploy/functions/release/index.ts | 2 +- src/deploy/functions/release/reporter.ts | 58 ++++++++++++- .../deploy/functions/release/reporter.spec.ts | 86 +++++++++++++++++-- src/track.ts | 5 +- 7 files changed, 171 insertions(+), 10 deletions(-) diff --git a/src/deploy/functions/args.ts b/src/deploy/functions/args.ts index 1138b9d9491..bc6e1e21db8 100644 --- a/src/deploy/functions/args.ts +++ b/src/deploy/functions/args.ts @@ -2,6 +2,7 @@ import * as backend from "./backend"; import * as gcfV2 from "../../gcp/cloudfunctionsv2"; import * as projectConfig from "../../functions/projectConfig"; import * as deployHelper from "./functionsDeployHelper"; +import { Runtime } from "./runtimes"; // These types should probably be in a root deploy.ts, but we can only boil the ocean one bit at a time. interface CodebasePayload { @@ -49,6 +50,19 @@ export interface Context { gcfV1: string[]; gcfV2: string[]; }; + + // Tracks metrics about codebase deployments to send to GA4 + codebaseDeployEvents?: Record; +} + +export interface CodebaseDeployEvent { + params?: "env_only" | "with_secrets" | "none"; + runtime?: Runtime; + runtime_notice?: string; + fn_deploy_num_successes: number; + fn_deploy_num_failures: number; + fn_deploy_num_canceled: number; + fn_deploy_num_skipped: number; } export interface FirebaseConfig { diff --git a/src/deploy/functions/build.ts b/src/deploy/functions/build.ts index 222b0a75d4e..846a49043b3 100644 --- a/src/deploy/functions/build.ts +++ b/src/deploy/functions/build.ts @@ -6,12 +6,14 @@ import { FirebaseError } from "../../error"; import { assertExhaustive, mapObject, nullsafeVisitor } from "../../functional"; import { UserEnvsOpts, writeUserEnvs } from "../../functions/env"; import { FirebaseConfig } from "./args"; +import { Runtime } from "./runtimes"; /* The union of a customer-controlled deployment and potentially deploy-time defined parameters */ export interface Build { requiredAPIs: RequiredApi[]; endpoints: Record; params: params.Param[]; + runtime?: Runtime; } /** diff --git a/src/deploy/functions/prepare.ts b/src/deploy/functions/prepare.ts index 32cad037653..7e92f0707a4 100644 --- a/src/deploy/functions/prepare.ts +++ b/src/deploy/functions/prepare.ts @@ -88,6 +88,8 @@ export async function prepare( runtimeConfig = { ...runtimeConfig, ...(await getFunctionsConfig(projectId)) }; } + context.codebaseDeployEvents = {}; + // ===Phase 1. Load codebases from source. const wantBuilds = await loadCodebases( context.config, @@ -155,15 +157,26 @@ export async function prepare( codebaseUsesEnvs.push(codebase); } + context.codebaseDeployEvents[codebase] = { + fn_deploy_num_successes: 0, + fn_deploy_num_failures: 0, + fn_deploy_num_canceled: 0, + fn_deploy_num_skipped: 0, + }; + if (wantBuild.params.length > 0) { if (wantBuild.params.every((p) => p.type !== "secret")) { + context.codebaseDeployEvents[codebase].params = "env_only"; void track("functions_params_in_build", "env_only"); } else { + context.codebaseDeployEvents[codebase].params = "with_secrets"; void track("functions_params_in_build", "with_secrets"); } } else { + context.codebaseDeployEvents[codebase].params = "none"; void track("functions_params_in_build", "none"); } + context.codebaseDeployEvents[codebase].runtime = wantBuild.runtime; } // ===Phase 2.5. Before proceeding further, let's make sure that we don't have conflicting function names. @@ -468,6 +481,7 @@ export async function loadCodebases( // in order for .init() calls to succeed. GOOGLE_CLOUD_QUOTA_PROJECT: projectId, }); + wantBuilds[codebase].runtime = codebaseConfig.runtime; } return wantBuilds; } diff --git a/src/deploy/functions/release/index.ts b/src/deploy/functions/release/index.ts index 08c46191304..e32749feda8 100644 --- a/src/deploy/functions/release/index.ts +++ b/src/deploy/functions/release/index.ts @@ -76,7 +76,7 @@ export async function release( const summary = await fab.applyPlan(plan); - await reporter.logAndTrackDeployStats(summary); + await reporter.logAndTrackDeployStats(summary, context); reporter.printErrors(summary); // N.B. Fabricator::applyPlan updates the endpoints it deploys to include the diff --git a/src/deploy/functions/release/reporter.ts b/src/deploy/functions/release/reporter.ts index 61611c0a7ba..88f73c64b5b 100644 --- a/src/deploy/functions/release/reporter.ts +++ b/src/deploy/functions/release/reporter.ts @@ -1,8 +1,9 @@ import * as backend from "../backend"; import * as clc from "colorette"; +import * as args from "../args"; import { logger } from "../../../logger"; -import { track } from "../../../track"; +import { track, trackGA4 } from "../../../track"; import * as utils from "../../../utils"; import { getFunctionLabel } from "../functionsDeployHelper"; @@ -56,7 +57,10 @@ export class AbortedDeploymentError extends DeploymentError { } /** Add debugger logs and GA metrics for deploy stats. */ -export async function logAndTrackDeployStats(summary: Summary): Promise { +export async function logAndTrackDeployStats( + summary: Summary, + context?: args.Context +): Promise { let totalTime = 0; let totalErrors = 0; let totalSuccesses = 0; @@ -64,22 +68,72 @@ export async function logAndTrackDeployStats(summary: Summary): Promise { const reports: Array> = []; const regions = new Set(); + const codebases = new Set(); for (const result of summary.results) { + const fnDeployEvent = { + platform: result.endpoint.platform, + trigger_type: backend.endpointTriggerType(result.endpoint), + region: result.endpoint.region, + runtime: result.endpoint.runtime, + status: !result.error + ? "success" + : result.error instanceof AbortedDeploymentError + ? "aborted" + : "failure", + duration: result.durationMs, + }; + reports.push(trackGA4("function_deploy", fnDeployEvent)); + const tag = triggerTag(result.endpoint); regions.add(result.endpoint.region); + codebases.add(result.endpoint.codebase || "default"); totalTime += result.durationMs; if (!result.error) { totalSuccesses++; + if ( + context?.codebaseDeployEvents && + context.codebaseDeployEvents[result.endpoint.codebase || "default"] !== undefined + ) { + context.codebaseDeployEvents[result.endpoint.codebase || "default"] + .fn_deploy_num_successes++; + } reports.push(track("function_deploy_success", tag, result.durationMs)); } else if (result.error instanceof AbortedDeploymentError) { totalAborts++; + if ( + context?.codebaseDeployEvents && + context.codebaseDeployEvents[result.endpoint.codebase || "default"] !== undefined + ) { + context.codebaseDeployEvents[result.endpoint.codebase || "default"] + .fn_deploy_num_canceled++; + } reports.push(track("function_deploy_abort", tag, result.durationMs)); } else { totalErrors++; + if ( + context?.codebaseDeployEvents && + context.codebaseDeployEvents[result.endpoint.codebase || "default"] !== undefined + ) { + context.codebaseDeployEvents[result.endpoint.codebase || "default"] + .fn_deploy_num_failures++; + } reports.push(track("function_deploy_failure", tag, result.durationMs)); } } + for (const codebase of codebases) { + if (context?.codebaseDeployEvents) { + reports.push(trackGA4("codebase_deploy", { ...context.codebaseDeployEvents[codebase] })); + } + } + const fnDeployGroupEvent = { + codebase_deploy_count: codebases.size >= 5 ? "5+" : codebases.size.toString(), + fn_deploy_num_successes: totalSuccesses, + fn_deploy_num_canceled: totalAborts, + fn_deploy_num_failures: totalErrors, + }; + reports.push(trackGA4("function_deploy_group", fnDeployGroupEvent)); + const regionCountTag = regions.size < 5 ? regions.size.toString() : ">=5"; reports.push(track("functions_region_count", regionCountTag, 1)); diff --git a/src/test/deploy/functions/release/reporter.spec.ts b/src/test/deploy/functions/release/reporter.spec.ts index ae2de5cd3f2..f006e62b424 100644 --- a/src/test/deploy/functions/release/reporter.spec.ts +++ b/src/test/deploy/functions/release/reporter.spec.ts @@ -6,6 +6,7 @@ import * as backend from "../../../../deploy/functions/backend"; import * as reporter from "../../../../deploy/functions/release/reporter"; import * as track from "../../../../track"; import * as events from "../../../../functions/events"; +import * as args from "../../../../deploy/functions/args"; const ENDPOINT_BASE: Omit = { platform: "gcfv1", @@ -117,10 +118,12 @@ describe("reporter", () => { }); describe("logAndTrackDeployStats", () => { + let trackGA4Stub: sinon.SinonStub; let trackStub: sinon.SinonStub; let debugStub: sinon.SinonStub; beforeEach(() => { + trackGA4Stub = sinon.stub(track, "trackGA4"); trackStub = sinon.stub(track, "track"); debugStub = sinon.stub(logger, "debug"); }); @@ -134,23 +137,94 @@ describe("reporter", () => { totalTime: 2_000, results: [ { - endpoint: ENDPOINT, + endpoint: { ...ENDPOINT, codebase: "codebase0" }, durationMs: 2_000, }, { - endpoint: ENDPOINT, + endpoint: { ...ENDPOINT, codebase: "codebase1" }, durationMs: 1_000, - error: new reporter.DeploymentError(ENDPOINT, "update", undefined), + error: new reporter.DeploymentError( + { ...ENDPOINT, codebase: "codebase1" }, + "update", + undefined + ), }, { - endpoint: ENDPOINT, + endpoint: { ...ENDPOINT, codebase: "codebase1" }, durationMs: 0, - error: new reporter.AbortedDeploymentError(ENDPOINT), + error: new reporter.AbortedDeploymentError({ ...ENDPOINT, codebase: "codebase1" }), }, ], }; - await reporter.logAndTrackDeployStats(summary); + const context: args.Context = { + projectId: "id", + codebaseDeployEvents: { + codebase0: { + params: "none", + fn_deploy_num_successes: 0, + fn_deploy_num_canceled: 0, + fn_deploy_num_failures: 0, + fn_deploy_num_skipped: 0, + }, + codebase1: { + params: "none", + fn_deploy_num_successes: 0, + fn_deploy_num_canceled: 0, + fn_deploy_num_failures: 0, + fn_deploy_num_skipped: 0, + }, + }, + }; + + await reporter.logAndTrackDeployStats(summary, context); + + expect(trackGA4Stub).to.have.been.calledWith("function_deploy", { + platform: "gcfv1", + trigger_type: "https", + region: "region", + runtime: "nodejs16", + status: "success", + duration: 2_000, + }); + expect(trackGA4Stub).to.have.been.calledWith("function_deploy", { + platform: "gcfv1", + trigger_type: "https", + region: "region", + runtime: "nodejs16", + status: "failure", + duration: 1_000, + }); + expect(trackGA4Stub).to.have.been.calledWith("function_deploy", { + platform: "gcfv1", + trigger_type: "https", + region: "region", + runtime: "nodejs16", + status: "aborted", + duration: 0, + }); + + expect(trackGA4Stub).to.have.been.calledWith("codebase_deploy", { + params: "none", + fn_deploy_num_successes: 1, + fn_deploy_num_canceled: 0, + fn_deploy_num_failures: 0, + fn_deploy_num_skipped: 0, + }); + expect(trackGA4Stub).to.have.been.calledWith("codebase_deploy", { + params: "none", + fn_deploy_num_successes: 0, + fn_deploy_num_canceled: 1, + fn_deploy_num_failures: 1, + fn_deploy_num_skipped: 0, + }); + + expect(trackGA4Stub).to.have.been.calledWith("function_deploy_group", { + codebase_deploy_count: "2", + fn_deploy_num_successes: 1, + fn_deploy_num_canceled: 1, + fn_deploy_num_failures: 1, + }); expect(trackStub).to.have.been.calledWith("functions_region_count", "1", 1); expect(trackStub).to.have.been.calledWith("function_deploy_success", "v1.https", 2_000); diff --git a/src/track.ts b/src/track.ts index c1aa68eaf33..702ba176b5a 100644 --- a/src/track.ts +++ b/src/track.ts @@ -14,7 +14,10 @@ type cliEventNames = | "login" | "api_enabled" | "hosting_version" - | "extension_added_to_manifest"; + | "extension_added_to_manifest" + | "function_deploy" + | "codebase_deploy" + | "function_deploy_group"; type GA4Property = "cli" | "emulator"; interface GA4Info { measurementId: string; From 2cfe24ba97adb4710950c915c1bd6b4053f2bc30 Mon Sep 17 00:00:00 2001 From: Brian Li Date: Wed, 28 Jun 2023 14:14:06 -0400 Subject: [PATCH 2/4] remove old track() calls --- src/deploy/functions/ensure.ts | 3 -- src/deploy/functions/prepare.ts | 15 ------ src/deploy/functions/release/reporter.ts | 47 ++----------------- .../node/parseRuntimeAndValidateSDK.ts | 3 -- .../functions/runtimes/node/versioning.ts | 2 - 5 files changed, 4 insertions(+), 66 deletions(-) diff --git a/src/deploy/functions/ensure.ts b/src/deploy/functions/ensure.ts index b48740bd748..7006e872af0 100644 --- a/src/deploy/functions/ensure.ts +++ b/src/deploy/functions/ensure.ts @@ -8,7 +8,6 @@ import { logLabeledBullet, logLabeledSuccess } from "../../utils"; import { ensureServiceAgentRole } from "../../gcp/secretManager"; import { getFirebaseProject } from "../../management/projects"; import { assertExhaustive } from "../../functional"; -import { track } from "../../track"; import * as backend from "./backend"; const FAQ_URL = "https://firebase.google.com/support/faq#functions-runtime"; @@ -37,7 +36,6 @@ export async function defaultServiceAccount(e: backend.Endpoint): Promise 0) { if (wantBuild.params.every((p) => p.type !== "secret")) { context.codebaseDeployEvents[codebase].params = "env_only"; - void track("functions_params_in_build", "env_only"); } else { context.codebaseDeployEvents[codebase].params = "with_secrets"; - void track("functions_params_in_build", "with_secrets"); } } else { context.codebaseDeployEvents[codebase].params = "none"; - void track("functions_params_in_build", "none"); } context.codebaseDeployEvents[codebase].runtime = wantBuild.runtime; } @@ -227,18 +224,6 @@ export async function prepare( inferBlockingDetails(wantBackend); } - const tag = hasUserConfig(runtimeConfig) - ? codebaseUsesEnvs.length > 0 - ? "mixed" - : "runtime_config" - : codebaseUsesEnvs.length > 0 - ? "dotenv" - : "none"; - void track("functions_codebase_deploy_env_method", tag); - - const codebaseCnt = Object.keys(payload.functions).length; - void track("functions_codebase_deploy_count", codebaseCnt >= 5 ? "5+" : codebaseCnt.toString()); - // ===Phase 5. Enable APIs required by the deploying backends. const wantBackend = backend.merge(...Object.values(wantBackends)); const haveBackend = backend.merge(...Object.values(haveBackends)); diff --git a/src/deploy/functions/release/reporter.ts b/src/deploy/functions/release/reporter.ts index 88f73c64b5b..10a4a64757c 100644 --- a/src/deploy/functions/release/reporter.ts +++ b/src/deploy/functions/release/reporter.ts @@ -3,7 +3,7 @@ import * as clc from "colorette"; import * as args from "../args"; import { logger } from "../../../logger"; -import { track, trackGA4 } from "../../../track"; +import { trackGA4 } from "../../../track"; import * as utils from "../../../utils"; import { getFunctionLabel } from "../functionsDeployHelper"; @@ -84,40 +84,27 @@ export async function logAndTrackDeployStats( }; reports.push(trackGA4("function_deploy", fnDeployEvent)); - const tag = triggerTag(result.endpoint); regions.add(result.endpoint.region); codebases.add(result.endpoint.codebase || "default"); totalTime += result.durationMs; if (!result.error) { totalSuccesses++; - if ( - context?.codebaseDeployEvents && - context.codebaseDeployEvents[result.endpoint.codebase || "default"] !== undefined - ) { + if (context?.codebaseDeployEvents?.[result.endpoint.codebase || "default"] !== undefined) { context.codebaseDeployEvents[result.endpoint.codebase || "default"] .fn_deploy_num_successes++; } - reports.push(track("function_deploy_success", tag, result.durationMs)); } else if (result.error instanceof AbortedDeploymentError) { totalAborts++; - if ( - context?.codebaseDeployEvents && - context.codebaseDeployEvents[result.endpoint.codebase || "default"] !== undefined - ) { + if (context?.codebaseDeployEvents?.[result.endpoint.codebase || "default"] !== undefined) { context.codebaseDeployEvents[result.endpoint.codebase || "default"] .fn_deploy_num_canceled++; } - reports.push(track("function_deploy_abort", tag, result.durationMs)); } else { totalErrors++; - if ( - context?.codebaseDeployEvents && - context.codebaseDeployEvents[result.endpoint.codebase || "default"] !== undefined - ) { + if (context?.codebaseDeployEvents?.[result.endpoint.codebase || "default"] !== undefined) { context.codebaseDeployEvents[result.endpoint.codebase || "default"] .fn_deploy_num_failures++; } - reports.push(track("function_deploy_failure", tag, result.durationMs)); } } @@ -134,38 +121,12 @@ export async function logAndTrackDeployStats( }; reports.push(trackGA4("function_deploy_group", fnDeployGroupEvent)); - const regionCountTag = regions.size < 5 ? regions.size.toString() : ">=5"; - reports.push(track("functions_region_count", regionCountTag, 1)); - - const gcfv1 = summary.results.find((r) => r.endpoint.platform === "gcfv1"); - const gcfv2 = summary.results.find((r) => r.endpoint.platform === "gcfv2"); - const tag = gcfv1 && gcfv2 ? "v1+v2" : gcfv1 ? "v1" : "v2"; - reports.push(track("functions_codebase_deploy", tag, summary.results.length)); - const avgTime = totalTime / (totalSuccesses + totalErrors); - logger.debug(`Total Function Deployment time: ${summary.totalTime}`); logger.debug(`${totalErrors + totalSuccesses + totalAborts} Functions Deployed`); logger.debug(`${totalErrors} Functions Errored`); logger.debug(`${totalAborts} Function Deployments Aborted`); logger.debug(`Average Function Deployment time: ${avgTime}`); - if (totalErrors + totalSuccesses > 0) { - if (totalErrors === 0) { - reports.push(track("functions_deploy_result", "success", totalSuccesses)); - } else if (totalSuccesses > 0) { - reports.push(track("functions_deploy_result", "partial_success", totalSuccesses)); - reports.push(track("functions_deploy_result", "partial_failure", totalErrors)); - reports.push( - track( - "functions_deploy_result", - "partial_error_ratio", - totalErrors / (totalSuccesses + totalErrors) - ) - ); - } else { - reports.push(track("functions_deploy_result", "failure", totalErrors)); - } - } await utils.allSettled(reports); } diff --git a/src/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.ts b/src/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.ts index 4ce21970fa8..58fd94e9a2c 100644 --- a/src/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.ts +++ b/src/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.ts @@ -2,7 +2,6 @@ import * as path from "path"; import * as clc from "colorette"; import { FirebaseError } from "../../../../error"; -import { track } from "../../../../track"; import * as runtimes from "../../runtimes"; // have to require this because no @types/cjson available @@ -80,7 +79,6 @@ export function getRuntimeChoice(sourceDir: string, runtimeFromConfig?: string): : UNSUPPORTED_NODE_VERSION_PACKAGE_JSON_MSG) + DEPRECATED_NODE_VERSION_INFO; if (!runtime || !ENGINE_RUNTIMES_NAMES.includes(runtime)) { - void track("functions_runtime_notices", "package_missing_runtime"); throw new FirebaseError(errorMessage, { exit: 1 }); } @@ -88,7 +86,6 @@ export function getRuntimeChoice(sourceDir: string, runtimeFromConfig?: string): // it's in ENGINE_RUNTIME_NAMES and not in DEPRECATED_RUNTIMES. This is still a // good defense in depth and also lets us upcast the response to Runtime safely. if (runtimes.isDeprecatedRuntime(runtime) || !runtimes.isValidRuntime(runtime)) { - void track("functions_runtime_notices", `${runtime}_deploy_prohibited`); throw new FirebaseError(errorMessage, { exit: 1 }); } diff --git a/src/deploy/functions/runtimes/node/versioning.ts b/src/deploy/functions/runtimes/node/versioning.ts index d0c400c9bbb..4945309c11f 100644 --- a/src/deploy/functions/runtimes/node/versioning.ts +++ b/src/deploy/functions/runtimes/node/versioning.ts @@ -6,7 +6,6 @@ import * as spawn from "cross-spawn"; import * as semver from "semver"; import { logger } from "../../../../logger"; -import { track } from "../../../../track"; import * as utils from "../../../../utils"; interface NpmShowResult { @@ -113,7 +112,6 @@ export function getLatestSDKVersion(): string | undefined { export function checkFunctionsSDKVersion(currentVersion: string): void { try { if (semver.lt(currentVersion, MIN_SDK_VERSION)) { - void track("functions_runtime_notices", "functions_sdk_too_old"); utils.logWarning(FUNCTIONS_SDK_VERSION_TOO_OLD_WARNING); } From 4dc0ed6d1bae3f9db8a1d982221aba47ae3f43df Mon Sep 17 00:00:00 2001 From: Brian Li Date: Wed, 28 Jun 2023 14:28:46 -0400 Subject: [PATCH 3/4] remove track fns unit tests --- .../deploy/functions/release/reporter.spec.ts | 81 ------------------- 1 file changed, 81 deletions(-) diff --git a/src/test/deploy/functions/release/reporter.spec.ts b/src/test/deploy/functions/release/reporter.spec.ts index f006e62b424..b18fee9f728 100644 --- a/src/test/deploy/functions/release/reporter.spec.ts +++ b/src/test/deploy/functions/release/reporter.spec.ts @@ -119,12 +119,10 @@ describe("reporter", () => { describe("logAndTrackDeployStats", () => { let trackGA4Stub: sinon.SinonStub; - let trackStub: sinon.SinonStub; let debugStub: sinon.SinonStub; beforeEach(() => { trackGA4Stub = sinon.stub(track, "trackGA4"); - trackStub = sinon.stub(track, "track"); debugStub = sinon.stub(logger, "debug"); }); @@ -226,88 +224,9 @@ describe("reporter", () => { fn_deploy_num_failures: 1, }); - expect(trackStub).to.have.been.calledWith("functions_region_count", "1", 1); - expect(trackStub).to.have.been.calledWith("function_deploy_success", "v1.https", 2_000); - expect(trackStub).to.have.been.calledWith("function_deploy_failure", "v1.https", 1_000); - // Aborts aren't tracked because they would throw off timing metrics - expect(trackStub).to.not.have.been.calledWith("function_deploy_failure", "v1.https", 0); - - expect(debugStub).to.have.been.calledWith("Total Function Deployment time: 2000"); - expect(debugStub).to.have.been.calledWith("3 Functions Deployed"); - expect(debugStub).to.have.been.calledWith("1 Functions Errored"); - expect(debugStub).to.have.been.calledWith("1 Function Deployments Aborted"); - // The 0ms for an aborted function isn't counted. expect(debugStub).to.have.been.calledWith("Average Function Deployment time: 1500"); }); - - it("tracks v1 vs v2 codebases", async () => { - const v1 = { ...ENDPOINT }; - const v2: backend.Endpoint = { ...ENDPOINT, platform: "gcfv2" }; - - const summary: reporter.Summary = { - totalTime: 1_000, - results: [ - { - endpoint: v1, - durationMs: 1_000, - }, - { - endpoint: v2, - durationMs: 1_000, - }, - ], - }; - - await reporter.logAndTrackDeployStats(summary); - expect(trackStub).to.have.been.calledWith("functions_codebase_deploy", "v1+v2", 2); - trackStub.resetHistory(); - - summary.results = [{ endpoint: v1, durationMs: 1_000 }]; - await reporter.logAndTrackDeployStats(summary); - expect(trackStub).to.have.been.calledWith("functions_codebase_deploy", "v1", 1); - trackStub.resetHistory(); - - summary.results = [{ endpoint: v2, durationMs: 1_000 }]; - await reporter.logAndTrackDeployStats(summary); - expect(trackStub).to.have.been.calledWith("functions_codebase_deploy", "v2", 1); - }); - - it("tracks overall success/failure", async () => { - const success: reporter.DeployResult = { - endpoint: ENDPOINT, - durationMs: 1_000, - }; - const failure: reporter.DeployResult = { - endpoint: ENDPOINT, - durationMs: 1_000, - error: new reporter.DeploymentError(ENDPOINT, "create", undefined), - }; - - const summary: reporter.Summary = { - totalTime: 1_000, - results: [success, failure], - }; - - await reporter.logAndTrackDeployStats(summary); - expect(trackStub).to.have.been.calledWith("functions_deploy_result", "partial_success", 1); - expect(trackStub).to.have.been.calledWith("functions_deploy_result", "partial_failure", 1); - expect(trackStub).to.have.been.calledWith( - "functions_deploy_result", - "partial_error_ratio", - 0.5 - ); - trackStub.resetHistory(); - - summary.results = [success]; - await reporter.logAndTrackDeployStats(summary); - expect(trackStub).to.have.been.calledWith("functions_deploy_result", "success", 1); - trackStub.resetHistory(); - - summary.results = [failure]; - await reporter.logAndTrackDeployStats(summary); - expect(trackStub).to.have.been.calledWith("functions_deploy_result", "failure", 1); - }); }); describe("printErrors", () => { From 13df09b0f5358bbaa3678239f3a757e5240b1808 Mon Sep 17 00:00:00 2001 From: Brian Li Date: Wed, 28 Jun 2023 14:35:13 -0400 Subject: [PATCH 4/4] cleanup --- src/deploy/functions/prepare.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/deploy/functions/prepare.ts b/src/deploy/functions/prepare.ts index 4f331c4f240..87ab96f9333 100644 --- a/src/deploy/functions/prepare.ts +++ b/src/deploy/functions/prepare.ts @@ -21,7 +21,6 @@ import { logLabeledBullet } from "../../utils"; import { getFunctionsConfig, prepareFunctionsUpload } from "./prepareFunctionsUpload"; import { promptForFailurePolicies, promptForMinInstances } from "./prompts"; import { needProjectId, needProjectNumber } from "../../projectUtils"; -import { track } from "../../track"; import { logger } from "../../logger"; import { ensureTriggerRegions } from "./triggerRegionHelper"; import { ensureServiceAgentRoles } from "./checkIam"; @@ -38,11 +37,6 @@ import { allEndpoints, Backend } from "./backend"; import { assertExhaustive } from "../../functional"; export const EVENTARC_SOURCE_ENV = "EVENTARC_CLOUD_EVENT_SOURCE"; -function hasUserConfig(config: Record): boolean { - // "firebase" key is always going to exist in runtime config. - // If any other key exists, we can assume that user is using runtime config. - return Object.keys(config).length > 1; -} /** * Prepare functions codebases for deploy.