From e91f86c2483d7fc858fefbb2ef4c33cb19e1163d Mon Sep 17 00:00:00 2001 From: Coco Sheng Date: Tue, 10 Mar 2026 10:59:13 -0400 Subject: [PATCH] feat(telemetry): add specific PR, issue, and custom tracking IDs for GitHub Actions (#21129) --- docs/cli/telemetry.md | 6 + .../clearcut-logger/clearcut-logger.test.ts | 107 ++++++++++++++++++ .../clearcut-logger/clearcut-logger.ts | 60 ++++++++++ .../clearcut-logger/event-metadata-key.ts | 14 ++- 4 files changed, 186 insertions(+), 1 deletion(-) diff --git a/docs/cli/telemetry.md b/docs/cli/telemetry.md index c812d37965..c254f04a29 100644 --- a/docs/cli/telemetry.md +++ b/docs/cli/telemetry.md @@ -339,6 +339,12 @@ Captures startup configuration and user prompt submissions. - `mcp_tools` (string, if applicable) - `mcp_tools_count` (int, if applicable) - `output_format` ("text", "json", or "stream-json") + - `github_workflow_name` (string, optional) + - `github_repository_hash` (string, optional) + - `github_event_name` (string, optional) + - `github_pr_number` (string, optional) + - `github_issue_number` (string, optional) + - `github_custom_tracking_id` (string, optional) - `gemini_cli.user_prompt`: Emitted when a user submits a prompt. - **Attributes**: diff --git a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.test.ts b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.test.ts index 195c5544bf..93eebd651e 100644 --- a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.test.ts +++ b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.test.ts @@ -195,6 +195,9 @@ describe('ClearcutLogger', () => { vi.stubEnv('MONOSPACE_ENV', ''); vi.stubEnv('REPLIT_USER', ''); vi.stubEnv('__COG_BASHRC_SOURCED', ''); + vi.stubEnv('GH_PR_NUMBER', ''); + vi.stubEnv('GH_ISSUE_NUMBER', ''); + vi.stubEnv('GH_CUSTOM_TRACKING_ID', ''); }); function setup({ @@ -596,6 +599,110 @@ describe('ClearcutLogger', () => { }); }); + describe('GITHUB_EVENT_NAME metadata', () => { + it('includes event name when GITHUB_EVENT_NAME is set', () => { + const { logger } = setup({}); + vi.stubEnv('GITHUB_EVENT_NAME', 'issues'); + + const event = logger?.createLogEvent(EventNames.API_ERROR, []); + expect(event?.event_metadata[0]).toContainEqual({ + gemini_cli_key: EventMetadataKey.GEMINI_CLI_GH_EVENT_NAME, + value: 'issues', + }); + }); + + it('does not include event name when GITHUB_EVENT_NAME is not set', () => { + const { logger } = setup({}); + vi.stubEnv('GITHUB_EVENT_NAME', undefined); + + const event = logger?.createLogEvent(EventNames.API_ERROR, []); + const hasEventName = event?.event_metadata[0].some( + (item) => + item.gemini_cli_key === EventMetadataKey.GEMINI_CLI_GH_EVENT_NAME, + ); + expect(hasEventName).toBe(false); + }); + }); + + describe('GH_PR_NUMBER metadata', () => { + it('includes PR number when GH_PR_NUMBER is set', () => { + vi.stubEnv('GH_PR_NUMBER', '123'); + const { logger } = setup({}); + + const event = logger?.createLogEvent(EventNames.API_ERROR, []); + + expect(event?.event_metadata[0]).toContainEqual({ + gemini_cli_key: EventMetadataKey.GEMINI_CLI_GH_PR_NUMBER, + value: '123', + }); + }); + + it('does not include PR number when GH_PR_NUMBER is not set', () => { + vi.stubEnv('GH_PR_NUMBER', undefined); + const { logger } = setup({}); + + const event = logger?.createLogEvent(EventNames.API_ERROR, []); + const hasPRNumber = event?.event_metadata[0].some( + (item) => + item.gemini_cli_key === EventMetadataKey.GEMINI_CLI_GH_PR_NUMBER, + ); + expect(hasPRNumber).toBe(false); + }); + }); + + describe('GH_ISSUE_NUMBER metadata', () => { + it('includes issue number when GH_ISSUE_NUMBER is set', () => { + vi.stubEnv('GH_ISSUE_NUMBER', '456'); + const { logger } = setup({}); + + const event = logger?.createLogEvent(EventNames.API_ERROR, []); + + expect(event?.event_metadata[0]).toContainEqual({ + gemini_cli_key: EventMetadataKey.GEMINI_CLI_GH_ISSUE_NUMBER, + value: '456', + }); + }); + + it('does not include issue number when GH_ISSUE_NUMBER is not set', () => { + vi.stubEnv('GH_ISSUE_NUMBER', undefined); + const { logger } = setup({}); + + const event = logger?.createLogEvent(EventNames.API_ERROR, []); + const hasIssueNumber = event?.event_metadata[0].some( + (item) => + item.gemini_cli_key === EventMetadataKey.GEMINI_CLI_GH_ISSUE_NUMBER, + ); + expect(hasIssueNumber).toBe(false); + }); + }); + + describe('GH_CUSTOM_TRACKING_ID metadata', () => { + it('includes custom tracking ID when GH_CUSTOM_TRACKING_ID is set', () => { + vi.stubEnv('GH_CUSTOM_TRACKING_ID', 'abc-789'); + const { logger } = setup({}); + + const event = logger?.createLogEvent(EventNames.API_ERROR, []); + + expect(event?.event_metadata[0]).toContainEqual({ + gemini_cli_key: EventMetadataKey.GEMINI_CLI_GH_CUSTOM_TRACKING_ID, + value: 'abc-789', + }); + }); + + it('does not include custom tracking ID when GH_CUSTOM_TRACKING_ID is not set', () => { + vi.stubEnv('GH_CUSTOM_TRACKING_ID', undefined); + const { logger } = setup({}); + + const event = logger?.createLogEvent(EventNames.API_ERROR, []); + const hasTrackingId = event?.event_metadata[0].some( + (item) => + item.gemini_cli_key === + EventMetadataKey.GEMINI_CLI_GH_CUSTOM_TRACKING_ID, + ); + expect(hasTrackingId).toBe(false); + }); + }); + describe('GITHUB_REPOSITORY metadata', () => { it('includes hashed repository when GITHUB_REPOSITORY is set', () => { vi.stubEnv('GITHUB_REPOSITORY', 'google/gemini-cli'); diff --git a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts index 310622aea4..4684969c13 100644 --- a/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts +++ b/packages/core/src/telemetry/clearcut-logger/clearcut-logger.ts @@ -190,6 +190,34 @@ function determineGHRepositoryName(): string | undefined { return process.env['GITHUB_REPOSITORY']; } +/** + * Determines the GitHub event name if the CLI is running in a GitHub Actions environment. + */ +function determineGHEventName(): string | undefined { + return process.env['GITHUB_EVENT_NAME']; +} + +/** + * Determines the GitHub Pull Request number if the CLI is running in a GitHub Actions environment. + */ +function determineGHPRNumber(): string | undefined { + return process.env['GH_PR_NUMBER']; +} + +/** + * Determines the GitHub Issue number if the CLI is running in a GitHub Actions environment. + */ +function determineGHIssueNumber(): string | undefined { + return process.env['GH_ISSUE_NUMBER']; +} + +/** + * Determines the GitHub custom tracking ID if the CLI is running in a GitHub Actions environment. + */ +function determineGHCustomTrackingId(): string | undefined { + return process.env['GH_CUSTOM_TRACKING_ID']; +} + /** * Clearcut URL to send logging events to. */ @@ -372,6 +400,10 @@ export class ClearcutLogger { const email = this.userAccountManager.getCachedGoogleAccount(); const surface = determineSurface(); const ghWorkflowName = determineGHWorkflowName(); + const ghEventName = determineGHEventName(); + const ghPRNumber = determineGHPRNumber(); + const ghIssueNumber = determineGHIssueNumber(); + const ghCustomTrackingId = determineGHCustomTrackingId(); const baseMetadata: EventValue[] = [ ...data, { @@ -406,6 +438,34 @@ export class ClearcutLogger { }); } + if (ghEventName) { + baseMetadata.push({ + gemini_cli_key: EventMetadataKey.GEMINI_CLI_GH_EVENT_NAME, + value: ghEventName, + }); + } + + if (ghPRNumber) { + baseMetadata.push({ + gemini_cli_key: EventMetadataKey.GEMINI_CLI_GH_PR_NUMBER, + value: ghPRNumber, + }); + } + + if (ghIssueNumber) { + baseMetadata.push({ + gemini_cli_key: EventMetadataKey.GEMINI_CLI_GH_ISSUE_NUMBER, + value: ghIssueNumber, + }); + } + + if (ghCustomTrackingId) { + baseMetadata.push({ + gemini_cli_key: EventMetadataKey.GEMINI_CLI_GH_CUSTOM_TRACKING_ID, + value: ghCustomTrackingId, + }); + } + const logEvent: LogEvent = { console_type: 'GEMINI_CLI', application: 102, // GEMINI_CLI diff --git a/packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts b/packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts index 43bfa3278d..473b8db524 100644 --- a/packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts +++ b/packages/core/src/telemetry/clearcut-logger/event-metadata-key.ts @@ -7,7 +7,7 @@ // Defines valid event metadata keys for Clearcut logging. export enum EventMetadataKey { // Deleted enums: 24 - // Next ID: 176 + // Next ID: 180 GEMINI_CLI_KEY_UNKNOWN = 0, @@ -231,6 +231,18 @@ export enum EventMetadataKey { // Logs the repository name of the GitHub Action that triggered the session. GEMINI_CLI_GH_REPOSITORY_NAME_HASH = 132, + // Logs the event name of the GitHub Action that triggered the session. + GEMINI_CLI_GH_EVENT_NAME = 176, + + // Logs the Pull Request number if the workflow is operating on a PR. + GEMINI_CLI_GH_PR_NUMBER = 177, + + // Logs the Issue number if the workflow is operating on an Issue. + GEMINI_CLI_GH_ISSUE_NUMBER = 178, + + // Logs a custom tracking string (e.g. a comma-separated list of issue IDs for scheduled batches). + GEMINI_CLI_GH_CUSTOM_TRACKING_ID = 179, + // ========================================================================== // Loop Detected Event Keys // ===========================================================================