diff --git a/.changeset/silent-readers-behave.md b/.changeset/silent-readers-behave.md new file mode 100644 index 000000000000..8b5e7e4b548d --- /dev/null +++ b/.changeset/silent-readers-behave.md @@ -0,0 +1,5 @@ +--- +'@astrojs/telemetry': patch +--- + +Detect package manager, improve types diff --git a/packages/create-astro/package.json b/packages/create-astro/package.json index 52d67fb980f9..c3bad7fd0e44 100644 --- a/packages/create-astro/package.json +++ b/packages/create-astro/package.json @@ -28,19 +28,21 @@ "create-astro.js" ], "dependencies": { - "@types/degit": "^2.8.3", - "@types/prompts": "^2.0.14", "chalk": "^5.0.1", "degit": "^2.8.4", "execa": "^6.1.0", "kleur": "^4.1.4", "ora": "^6.1.0", "prompts": "^2.4.2", + "which-pm-runs": "^1.1.0", "yargs-parser": "^21.0.1" }, "devDependencies": { "@types/chai": "^4.3.1", + "@types/degit": "^2.8.3", "@types/mocha": "^9.1.1", + "@types/prompts": "^2.0.14", + "@types/which-pm-runs": "^1.0.0", "@types/yargs-parser": "^21.0.0", "astro-scripts": "workspace:*", "chai": "^4.3.6", diff --git a/packages/create-astro/src/index.ts b/packages/create-astro/src/index.ts index 7c69f5438bbf..5399d7832bd7 100644 --- a/packages/create-astro/src/index.ts +++ b/packages/create-astro/src/index.ts @@ -10,6 +10,7 @@ import yargs from 'yargs-parser'; import { loadWithRocketGradient, rocketAscii } from './gradient.js'; import { defaultLogLevel, logger } from './logger.js'; import { TEMPLATES } from './templates.js'; +import detectPackageManager from 'which-pm-runs'; function wait(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); @@ -48,7 +49,7 @@ const FILES_TO_REMOVE = ['.stackblitzrc', 'sandbox.config.json', 'CHANGELOG.md'] // Please also update the installation instructions in the docs at https://github.com/withastro/docs/blob/main/src/pages/en/install/auto.md if you make any changes to the flow or wording here. export async function main() { - const pkgManager = pkgManagerFromUserAgent(process.env.npm_config_user_agent); + const pkgManager = detectPackageManager()?.name || 'npm'; logger.debug('Verbose logging turned on'); console.log(`\n${bold('Welcome to Astro!')} ${gray(`(create-astro v${version})`)}`); @@ -251,18 +252,3 @@ function emojiWithFallback(char: string, fallback: string) { return process.platform !== 'win32' ? char : fallback; } -function pkgManagerFromUserAgent(userAgent?: string) { - if (!userAgent) return 'npm'; - const pkgSpec = userAgent.split(' ')[0]; - const pkgSpecArr = pkgSpec.split('/'); - return pkgSpecArr[0]; -} - -function pkgManagerExecCommand(pkgManager: string) { - if (pkgManager === 'pnpm') { - return 'pnpx'; - } else { - // note: yarn does not have an "npx" equivalent - return 'npx'; - } -} diff --git a/packages/telemetry/package.json b/packages/telemetry/package.json index 7785a8797c45..534643654bae 100644 --- a/packages/telemetry/package.json +++ b/packages/telemetry/package.json @@ -34,11 +34,13 @@ "git-up": "^4.0.5", "is-docker": "^3.0.0", "is-wsl": "^2.2.0", - "node-fetch": "^3.2.5" + "node-fetch": "^3.2.5", + "which-pm-runs": "^1.1.0" }, "devDependencies": { "@types/dlv": "^1.1.2", "@types/node": "^14.18.21", + "@types/which-pm-runs": "^1.0.0", "astro-scripts": "workspace:*" }, "engines": { diff --git a/packages/telemetry/src/index.ts b/packages/telemetry/src/index.ts index 0bba53e5ab09..583d7cae286f 100644 --- a/packages/telemetry/src/index.ts +++ b/packages/telemetry/src/index.ts @@ -9,15 +9,12 @@ import { getSystemInfo, SystemInfo } from './system-info.js'; export type AstroTelemetryOptions = { astroVersion: string; viteVersion: string }; export type TelemetryEvent = { eventName: string; payload: Record }; -interface EventContext { + +interface EventMeta extends SystemInfo {} +interface EventContext extends ProjectInfo { anonymousId: string; - anonymousProjectId: string; anonymousSessionId: string; } - -interface EventMeta extends SystemInfo { - isGit: boolean; -} export class AstroTelemetry { private _anonymousSessionId: string | undefined; private _anonymousProjectInfo: ProjectInfo | undefined; @@ -118,29 +115,19 @@ export class AstroTelemetry { return Promise.resolve(); } - if (this.debug.enabled) { - // Print to standard error to simplify selecting the output - events.forEach(({ eventName, payload }) => - this.debug(JSON.stringify({ eventName, payload }, null, 2)) - ); - // Do not send the telemetry data if debugging. Users may use this feature - // to preview what data would be sent. - return Promise.resolve(); - } - // Skip recording telemetry if the feature is disabled if (this.isDisabled) { + this.debug('telemetry disabled'); return Promise.resolve(); } const meta: EventMeta = { ...getSystemInfo({ astroVersion: this.astroVersion, viteVersion: this.viteVersion }), - isGit: this.anonymousProjectInfo.isGit, }; const context: EventContext = { + ...this.anonymousProjectInfo, anonymousId: this.anonymousId, - anonymousProjectId: this.anonymousProjectInfo.anonymousProjectId, anonymousSessionId: this.anonymousSessionId, }; @@ -150,6 +137,15 @@ export class AstroTelemetry { context.anonymousId = `CI.${meta.ciName || 'UNKNOWN'}`; } + if (this.debug.enabled) { + // Print to standard error to simplify selecting the output + this.debug({ context, meta }); + this.debug(JSON.stringify(events, null, 2)); + // Do not send the telemetry data if debugging. Users may use this feature + // to preview what data would be sent. + return Promise.resolve(); + } + return post({ context, meta, diff --git a/packages/telemetry/src/project-info.ts b/packages/telemetry/src/project-info.ts index 7666a8145d72..5f95d1b4b281 100644 --- a/packages/telemetry/src/project-info.ts +++ b/packages/telemetry/src/project-info.ts @@ -1,6 +1,7 @@ import { execSync } from 'child_process'; import type { BinaryLike } from 'node:crypto'; import { createHash } from 'node:crypto'; +import detectPackageManager from 'which-pm-runs'; /** * Astro Telemetry -- Project Info @@ -46,9 +47,13 @@ import { createHash } from 'node:crypto'; export interface ProjectInfo { /* Your unique project identifier. This will be hashed again before sending. */ - anonymousProjectId: string; + anonymousProjectId: string | undefined; /* true if your project is connected to a git repository. false otherwise. */ isGit: boolean; + /* The package manager used to run Astro */ + packageManager: string | undefined; + /* The version of the package manager used to run Astro */ + packageManagerVersion: string | undefined; } function createAnonymousValue(payload: BinaryLike): string { @@ -75,7 +80,7 @@ function getProjectIdFromGit(): string | null { } } -export function getProjectInfo(isCI: boolean): ProjectInfo { +function getProjectId(isCI: boolean): Pick { const projectIdFromGit = getProjectIdFromGit(); if (projectIdFromGit) { return { @@ -83,8 +88,29 @@ export function getProjectInfo(isCI: boolean): ProjectInfo { anonymousProjectId: createAnonymousValue(projectIdFromGit), }; } + // If we're running in CI, the current working directory is not unique. + // If the cwd is a single level deep (ex: '/app'), it's probably not unique. + const cwd = process.cwd(); + const isCwdGeneric = (cwd.match(/[\/|\\]/g) || []).length === 1; + if (isCI || isCwdGeneric) { + return { + isGit: false, + anonymousProjectId: undefined, + }; + } return { isGit: false, - anonymousProjectId: isCI ? '' : createAnonymousValue(process.cwd()), + anonymousProjectId: createAnonymousValue(cwd), + }; +} + +export function getProjectInfo(isCI: boolean): ProjectInfo { + const projectId = getProjectId(isCI); + const packageManager = detectPackageManager(); + return { + ...projectId, + packageManager: packageManager?.name, + packageManagerVersion: packageManager?.version, }; } +// diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 12adbbf8fe0c..9aa6f057af0a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1844,6 +1844,7 @@ importers: '@types/degit': ^2.8.3 '@types/mocha': ^9.1.1 '@types/prompts': ^2.0.14 + '@types/which-pm-runs': ^1.0.0 '@types/yargs-parser': ^21.0.0 astro-scripts: workspace:* chai: ^4.3.6 @@ -1855,20 +1856,23 @@ importers: ora: ^6.1.0 prompts: ^2.4.2 uvu: ^0.5.3 + which-pm-runs: ^1.1.0 yargs-parser: ^21.0.1 dependencies: - '@types/degit': 2.8.3 - '@types/prompts': 2.0.14 chalk: 5.0.1 degit: 2.8.4 execa: 6.1.0 kleur: 4.1.5 ora: 6.1.2 prompts: 2.4.2 + which-pm-runs: 1.1.0 yargs-parser: 21.0.1 devDependencies: '@types/chai': 4.3.1 + '@types/degit': 2.8.3 '@types/mocha': 9.1.1 + '@types/prompts': 2.0.14 + '@types/which-pm-runs': 1.0.0 '@types/yargs-parser': 21.0.0 astro-scripts: link:../../scripts chai: 4.3.6 @@ -2310,6 +2314,7 @@ importers: specifiers: '@types/dlv': ^1.1.2 '@types/node': ^14.18.21 + '@types/which-pm-runs': ^1.0.0 astro-scripts: workspace:* ci-info: ^3.3.1 debug: ^4.3.4 @@ -2320,6 +2325,7 @@ importers: is-docker: ^3.0.0 is-wsl: ^2.2.0 node-fetch: ^3.2.5 + which-pm-runs: ^1.1.0 dependencies: ci-info: 3.3.2 debug: 4.3.4 @@ -2330,9 +2336,11 @@ importers: is-docker: 3.0.0 is-wsl: 2.2.0 node-fetch: 3.2.6 + which-pm-runs: 1.1.0 devDependencies: '@types/dlv': 1.1.2 '@types/node': 14.18.21 + '@types/which-pm-runs': 1.0.0 astro-scripts: link:../../scripts packages/webapi: @@ -7385,7 +7393,7 @@ packages: /@types/degit/2.8.3: resolution: {integrity: sha512-CL7y71j2zaDmtPLD5Xq5S1Gv2dFoHl0/GBZm6s39Mj/ls28L3NzAOqf7H4H0/2TNVMgMjMVf9CAFYSjmXhi3bw==} - dev: false + dev: true /@types/diff/5.0.2: resolution: {integrity: sha512-uw8eYMIReOwstQ0QKF0sICefSy8cNO/v7gOTiIy9SbwuHyEecJUm7qlgueOO5S1udZ5I/irVydHVwMchgzbKTg==} @@ -7536,7 +7544,7 @@ packages: resolution: {integrity: sha512-HZBd99fKxRWpYCErtm2/yxUZv6/PBI9J7N4TNFffl5JbrYMHBwF25DjQGTW3b3jmXq+9P6/8fCIb2ee57BFfYA==} dependencies: '@types/node': 18.0.0 - dev: false + dev: true /@types/prop-types/15.7.5: resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} @@ -7642,6 +7650,10 @@ packages: /@types/unist/2.0.6: resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} + /@types/which-pm-runs/1.0.0: + resolution: {integrity: sha512-BXfdlYLWvRhngJbih4N57DjO+63Z7AxiFiip8yq3rD46U7V4I2W538gngPvBsZiMehhD8sfGf4xLI6k7TgXvNw==} + dev: true + /@types/yargs-parser/21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} dev: true @@ -15157,6 +15169,11 @@ packages: resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==} dev: true + /which-pm-runs/1.1.0: + resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} + engines: {node: '>=4'} + dev: false + /which-pm/2.0.0: resolution: {integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==} engines: {node: '>=8.15'}