Skip to content

Commit

Permalink
Telemetry (#3256)
Browse files Browse the repository at this point in the history
* feat: add @astrojs/telemetry

* feat: add telemetry events, add queueing system

* feat(telemetry): record CLI events

* chore: add note

* feat: support generic TELEMETRY_DISABLED env var

* Fix test script

* shim telemetry in tests

* Shim telemetry in other commands

* Stub telemetry in the memory leak test

* Disable telemetry in smoke tests

* Adds a changeset

* Run the formatter

* few updates

* Include config keys

* Add shallow viteKeys array:
:

* Add vite keys and tests

Co-authored-by: Nate Moore <[email protected]>
  • Loading branch information
matthewp and Nate Moore committed May 3, 2022
1 parent 48a35e6 commit f76038a
Show file tree
Hide file tree
Showing 28 changed files with 938 additions and 35 deletions.
6 changes: 6 additions & 0 deletions .changeset/lazy-phones-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@astrojs/telemetry': minor
'astro': patch
---

Adds anonymous telemetry data to the cli
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ jobs:
test:
name: 'Test: ${{ matrix.os }} (node@${{ matrix.node_version }})'
runs-on: ${{ matrix.os }}
env:
ASTRO_TELEMETRY_DISABLED: true
strategy:
matrix:
os: [ubuntu-latest]
Expand Down
1 change: 1 addition & 0 deletions packages/astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"@astrojs/language-server": "^0.13.4",
"@astrojs/markdown-remark": "^0.9.2",
"@astrojs/prism": "0.4.1",
"@astrojs/telemetry": "^0.0.1",
"@astrojs/webapi": "^0.11.1",
"@babel/core": "^7.17.9",
"@babel/generator": "^7.17.9",
Expand Down
63 changes: 58 additions & 5 deletions packages/astro/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import { LogOptions } from '../core/logger/core.js';
import * as colors from 'kleur/colors';
import yargs from 'yargs-parser';
import { z } from 'zod';
import { AstroTelemetry } from '@astrojs/telemetry';
import * as event from '@astrojs/telemetry/events';

import { nodeLogDestination, enableVerboseLogging } from '../core/logger/node.js';
import build from '../core/build/index.js';
import add from '../core/add/index.js';
import devServer from '../core/dev/index.js';
import preview from '../core/preview/index.js';
import { check } from './check.js';
import { openInBrowser } from './open.js';
import * as telemetryHandler from './telemetry.js';
import { loadConfig } from '../core/config.js';
import { printHelp, formatErrorMessage, formatConfigErrorMessage } from '../core/messages.js';
import { createSafeError } from '../core/util.js';
Expand All @@ -27,7 +31,8 @@ type CLICommand =
| 'build'
| 'preview'
| 'reload'
| 'check';
| 'check'
| 'telemetry';

/** Display --help flag */
function printAstroHelp() {
Expand All @@ -41,6 +46,7 @@ function printAstroHelp() {
['build', 'Build a pre-compiled production-ready site.'],
['preview', 'Preview your build locally before deploying.'],
['check', 'Check your project for errors.'],
['telemetry', 'Enable/disable anonymous data collection.'],
['--version', 'Show the version number and exit.'],
['--help', 'Show this help message.'],
],
Expand All @@ -67,6 +73,7 @@ async function printVersion() {
function resolveCommand(flags: Arguments): CLICommand {
const cmd = flags._[2] as string;
if (cmd === 'add') return 'add';
if (cmd === 'telemetry') return 'telemetry';
if (flags.version) return 'version';
else if (flags.help) return 'help';

Expand Down Expand Up @@ -103,20 +110,42 @@ export async function cli(args: string[]) {
} else if (flags.silent) {
logging.level = 'silent';
}
const telemetry = new AstroTelemetry({ version: process.env.PACKAGE_VERSION ?? '' });

if (cmd === 'telemetry') {
try {
const subcommand = flags._[3]?.toString();
return await telemetryHandler.update(subcommand, { flags, telemetry });
} catch (err) {
return throwAndExit(err);
}
}

switch (cmd) {
case 'add': {
try {
const packages = flags._.slice(3) as string[];
return await add(packages, { cwd: root, flags, logging });
telemetry.record(
event.eventCliSession({
astroVersion: process.env.PACKAGE_VERSION ?? '',
cliCommand: 'add',
})
);
return await add(packages, { cwd: root, flags, logging, telemetry });
} catch (err) {
return throwAndExit(err);
}
}
case 'dev': {
try {
const config = await loadConfig({ cwd: root, flags, cmd });
await devServer(config, { logging });
telemetry.record(
event.eventCliSession(
{ astroVersion: process.env.PACKAGE_VERSION ?? '', cliCommand: 'dev' },
config
)
);
await devServer(config, { logging, telemetry });
return await new Promise(() => {}); // lives forever
} catch (err) {
return throwAndExit(err);
Expand All @@ -126,22 +155,40 @@ export async function cli(args: string[]) {
case 'build': {
try {
const config = await loadConfig({ cwd: root, flags, cmd });
return await build(config, { logging });
telemetry.record(
event.eventCliSession(
{ astroVersion: process.env.PACKAGE_VERSION ?? '', cliCommand: 'build' },
config
)
);
return await build(config, { logging, telemetry });
} catch (err) {
return throwAndExit(err);
}
}

case 'check': {
const config = await loadConfig({ cwd: root, flags, cmd });
telemetry.record(
event.eventCliSession(
{ astroVersion: process.env.PACKAGE_VERSION ?? '', cliCommand: 'check' },
config
)
);
const ret = await check(config);
return process.exit(ret);
}

case 'preview': {
try {
const config = await loadConfig({ cwd: root, flags, cmd });
const server = await preview(config, { logging });
telemetry.record(
event.eventCliSession(
{ astroVersion: process.env.PACKAGE_VERSION ?? '', cliCommand: 'preview' },
config
)
);
const server = await preview(config, { logging, telemetry });
return await server.closed(); // keep alive until the server is closed
} catch (err) {
return throwAndExit(err);
Expand All @@ -150,6 +197,12 @@ export async function cli(args: string[]) {

case 'docs': {
try {
await telemetry.record(
event.eventCliSession({
astroVersion: process.env.PACKAGE_VERSION ?? '',
cliCommand: 'docs',
})
);
return await openInBrowser('https://docs.astro.build/');
} catch (err) {
return throwAndExit(err);
Expand Down
47 changes: 47 additions & 0 deletions packages/astro/src/cli/telemetry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-disable no-console */
import type yargs from 'yargs-parser';
import type { AstroTelemetry } from '@astrojs/telemetry';

import prompts from 'prompts';
import * as msg from '../core/messages.js';

export interface TelemetryOptions {
flags: yargs.Arguments;
telemetry: AstroTelemetry;
}

export async function update(subcommand: string, { flags, telemetry }: TelemetryOptions) {
const isValid = ['enable', 'disable', 'reset'].includes(subcommand);

if (flags.help || !isValid) {
msg.printHelp({
commandName: 'astro telemetry',
usage: '<enable|disable|reset>',
commands: [
['enable', 'Enable anonymous data collection.'],
['disable', 'Disable anonymous data collection.'],
['reset', 'Reset anonymous data collection settings.'],
],
});
return;
}

switch (subcommand) {
case 'enable': {
telemetry.setEnabled(true);
console.log(msg.telemetryEnabled());
return;
}
case 'disable': {
telemetry.setEnabled(false);
console.log(msg.telemetryDisabled());
return;
}
case 'reset': {
telemetry.clear();
console.log(msg.telemetryReset());
return;
}
}
}

4 changes: 3 additions & 1 deletion packages/astro/src/core/add/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type yargs from 'yargs-parser';
import type { AstroTelemetry } from '@astrojs/telemetry';
import path from 'path';
import { existsSync, promises as fs } from 'fs';
import { execa } from 'execa';
Expand All @@ -24,6 +25,7 @@ import { appendForwardSlash } from '../path.js';
export interface AddOptions {
logging: LogOptions;
flags: yargs.Arguments;
telemetry: AstroTelemetry;
cwd?: string;
}

Expand All @@ -33,7 +35,7 @@ export interface IntegrationInfo {
dependencies: [name: string, version: string][];
}

export default async function add(names: string[], { cwd, flags, logging }: AddOptions) {
export default async function add(names: string[], { cwd, flags, logging, telemetry }: AddOptions) {
if (flags.help) {
printHelp({
commandName: 'astro add',
Expand Down
7 changes: 3 additions & 4 deletions packages/astro/src/core/build/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { AstroConfig, BuildConfig, ManifestData } from '../../@types/astro';
import type { LogOptions } from '../logger/core';
import type { AstroTelemetry } from '@astrojs/telemetry';

import fs from 'fs';
import * as colors from 'kleur/colors';
Expand Down Expand Up @@ -33,13 +34,11 @@ import { fixViteErrorMessage } from '../errors.js';
export interface BuildOptions {
mode?: string;
logging: LogOptions;
telemetry: AstroTelemetry;
}

/** `astro build` */
export default async function build(
config: AstroConfig,
options: BuildOptions = { logging: nodeLogOptions }
): Promise<void> {
export default async function build(config: AstroConfig, options: BuildOptions): Promise<void> {
applyPolyfill();
const builder = new AstroBuilder(config, options);
await builder.run();
Expand Down
8 changes: 4 additions & 4 deletions packages/astro/src/core/dev/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { AddressInfo } from 'net';
import type { AstroTelemetry } from '@astrojs/telemetry';
import { performance } from 'perf_hooks';
import * as vite from 'vite';
import type { AstroConfig } from '../../@types/astro';
Expand All @@ -17,6 +18,7 @@ import { apply as applyPolyfill } from '../polyfill.js';

export interface DevOptions {
logging: LogOptions;
telemetry: AstroTelemetry;
}

export interface DevServer {
Expand All @@ -25,12 +27,10 @@ export interface DevServer {
}

/** `astro dev` */
export default async function dev(
config: AstroConfig,
options: DevOptions = { logging: nodeLogOptions }
): Promise<DevServer> {
export default async function dev(config: AstroConfig, options: DevOptions): Promise<DevServer> {
const devStart = performance.now();
applyPolyfill();
await options.telemetry.record([]);
config = await runHookConfigSetup({ config, command: 'dev' });
const { host, port } = config.server;
const viteConfig = await createVite(
Expand Down
47 changes: 38 additions & 9 deletions packages/astro/src/core/messages.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/**
* Dev server messages (organized here to prevent clutter)
*/

import type { AddressInfo } from 'net';
import type { AstroConfig } from '../@types/astro';
import os from 'os';
import {
bold,
dim,
Expand All @@ -15,10 +14,9 @@ import {
black,
bgRed,
bgWhite,
bgCyan,
} from 'kleur/colors';
import os from 'os';
import type { AddressInfo } from 'net';
import type { AstroConfig } from '../@types/astro';
import boxen from 'boxen';
import { collectErrorMetadata, cleanErrorStack } from './errors.js';
import { ZodError } from 'zod';
import { emoji, getLocalAddress, padMultilineString } from './util.js';
Expand Down Expand Up @@ -116,6 +114,37 @@ export function devStart({
return messages.map((msg) => ` ${msg}`).join('\n');
}

export function telemetryNotice() {
const headline = yellow(`Astro now collects ${bold('anonymous')} usage data.`);
const why = `This ${bold('optional program')} will help shape our roadmap.`;
const more = `For more info, visit ${underline('https://astro.build/telemetry')}`;
const box = boxen([headline, why, '', more].join('\n'), {
margin: 0,
padding: 1,
borderStyle: 'round',
borderColor: 'yellow',
});
return box;
}

export function telemetryEnabled() {
return `\n ${green('◉')} Anonymous telemetry is ${bgGreen(
black(' enabled ')
)}. Thank you for improving Astro!\n`;
}

export function telemetryDisabled() {
return `\n ${yellow('◯')} Anonymous telemetry is ${bgYellow(
black(' disabled ')
)}. We won't share any usage data.\n`;
}

export function telemetryReset() {
return `\n ${cyan('◆')} Anonymous telemetry has been ${bgCyan(
black(' reset ')
)}. You may be prompted again.\n`;
}

export function prerelease({ currentVersion }: { currentVersion: string }) {
const tag = currentVersion.split('-').slice(1).join('-').replace(/\..*$/, '');
const badge = bgYellow(black(` ${tag} `));
Expand Down Expand Up @@ -227,7 +256,7 @@ export function printHelp({
for (const row of rows) {
raw += `${opts.prefix}${bold(`${row[0]}`.padStart(opts.padding - opts.prefix.length))}`;
if (split) raw += '\n ';
raw += dim(row[1]) + '\n';
raw += ' ' + dim(row[1]) + '\n';
}

return raw.slice(0, -1); // remove latest \n
Expand All @@ -252,7 +281,7 @@ export function printHelp({
message.push(
linebreak(),
title('Commands'),
table(commands, { padding: 28, prefix: ' astro ' })
table(commands, { padding: 28, prefix: ` ${commandName || 'astro'} ` })
);
}

Expand Down
3 changes: 3 additions & 0 deletions packages/astro/src/core/preview/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { AstroConfig } from '../../@types/astro';
import type { LogOptions } from '../logger/core';
import type { AddressInfo } from 'net';
import type { AstroTelemetry } from '@astrojs/telemetry';

import http from 'http';
import sirv from 'sirv';
import { performance } from 'perf_hooks';
Expand All @@ -12,6 +14,7 @@ import { getResolvedHostForHttpServer } from './util.js';

interface PreviewOptions {
logging: LogOptions;
telemetry: AstroTelemetry;
}

export interface PreviewServer {
Expand Down
Loading

0 comments on commit f76038a

Please sign in to comment.