Skip to content

Track installation package manager in analytics#7348

Open
alfonso-noriega wants to merge 2 commits intomainfrom
track-install-package-manager
Open

Track installation package manager in analytics#7348
alfonso-noriega wants to merge 2 commits intomainfrom
track-install-package-manager

Conversation

@alfonso-noriega
Copy link
Copy Markdown
Contributor

Problem

~34% of CLI users (77K unique devices in the last 30 days) have unknown as their package manager in analytics. This happens because cmd_all_launcher and env_package_manager both rely on npm_config_user_agent, which is only set when a package manager spawns the CLI process. Users who run the shopify binary directly from the shell (the common case for global installs) never have this env var set.

Global installs are 85% of all CLI traffic, and 39% of those events have unknown package manager.

Solution

Add a new env_install_package_manager field to the analytics payload, populated by inferPackageManagerForGlobalCLI() — a function that was already written, tested, and used for upgrade logic, but never wired into analytics.

Detection logic (in priority order):

  1. SHOPIFY_HOMEBREW_FORMULA env var → homebrew (injected by the brew formula wrapper — most reliable)
  2. Resolved binary path contains yarn / pnpm / bun → respective PM
  3. Resolved binary path contains /cellar/homebrew (symlink resolved Intel/AS Mac path)
  4. Fallback → npm

Paths are already lowercased before matching, so Windows paths with mixed case (e.g. Yarn) are handled correctly.

What changes

  • analytics.ts: import and call inferPackageManagerForGlobalCLI(), add env_install_package_manager to EnvironmentData interface and return value
  • monorail.ts: add env_install_package_manager to the public payload schema

Note on existing fields

This is intentionally a new field rather than replacing env_package_manager. The two fields mean different things:

  • env_package_manager — the project's package manager (detected via lockfile in cwd)
  • env_install_package_manager — how the CLI itself was installed globally

Test plan

  • Existing tests pass (analytics.test.ts, is-global.test.ts)
  • Verify env_install_package_manager appears in Monorail events after deploy

🤖 Generated with Claude Code

Wire inferPackageManagerForGlobalCLI() into the analytics payload as
env_install_package_manager. This field correctly identifies how the CLI
was globally installed (homebrew via SHOPIFY_HOMEBREW_FORMULA env var,
or yarn/pnpm/bun via binary path inspection, falling back to npm).

Previously ~34% of users had unknown package manager in analytics because
packageManagerFromUserAgent() requires npm_config_user_agent, which is
only set when a package manager spawns the process — not when users run
the CLI binary directly from the shell (the common global install case).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@alfonso-noriega alfonso-noriega requested a review from a team as a code owner April 20, 2026 12:08
Aligns with Shopify/monorail#23445 which adds env_install_package_manager
to the schema.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

Differences in type declarations

We detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:

  • Some seemingly private modules might be re-exported through public modules.
  • If the branch is behind main you might see odd diffs, rebase main into this branch.

New type declarations

We found no new type declarations in this PR

Existing type declarations

packages/cli-kit/dist/private/node/analytics.d.ts
@@ -19,6 +19,7 @@ interface EnvironmentData {
     env_device_id: string;
     env_cloud: string;
     env_package_manager: string;
+    env_install_package_manager: string;
     env_is_global: boolean;
     env_auth_method: string;
     env_is_wsl: boolean;
packages/cli-kit/dist/public/node/monorail.d.ts
@@ -2,7 +2,7 @@ import { JsonMap } from '../../private/common/json.js';
 import { DeepRequired } from '../common/ts/deep-required.js';
 export { DeepRequired };
 type Optional<T> = T | null;
-export declare const MONORAIL_COMMAND_TOPIC = "app_cli3_command/1.21";
+export declare const MONORAIL_COMMAND_TOPIC = "app_cli3_command/1.22";
 export interface Schemas {
     [MONORAIL_COMMAND_TOPIC]: {
         sensitive: {
@@ -129,6 +129,7 @@ export interface Schemas {
             env_ci_platform?: Optional<string>;
             env_device_id?: Optional<string>;
             env_package_manager?: Optional<string>;
+            env_install_package_manager?: Optional<string>;
             env_package_manager_workspaces?: Optional<boolean>;
             env_plugin_installed_any_custom?: Optional<boolean>;
             env_plugin_installed_shopify?: Optional<string>;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant