Skip to content

Commit

Permalink
chore(crwrsca): implement basic telemetry (#11132)
Browse files Browse the repository at this point in the history
Co-authored-by: Tobbe Lundberg <tobbe@tlundberg.com>
  • Loading branch information
Josh-Walker-GM and Tobbe authored Aug 1, 2024
1 parent b11a798 commit dfbb132
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 4 deletions.
1 change: 1 addition & 0 deletions packages/create-redwood-rsc-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
},
"dependencies": {
"chalk": "5.3.0",
"ci-info": "4.0.0",
"enquirer": "2.4.1",
"execa": "9.3.0",
"node-fetch": "3.3.2",
Expand Down
9 changes: 9 additions & 0 deletions packages/create-redwood-rsc-app/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/usr/bin/env node

import type { TelemetryInfo } from './telemetry.js'

import { initConfig } from './config.js'
import { downloadTemplate } from './download.js'
import { handleError } from './error.js'
Expand All @@ -9,11 +11,16 @@ import { setInstallationDir } from './installationDir.js'
import { relaunchOnLatest, shouldRelaunch } from './latest.js'
import { printDone, printWelcome } from './messages.js'
import { checkNodeVersion, checkYarnInstallation } from './prerequisites.js'
import { sendTelemetry } from './telemetry.js'
import { upgradeToLatestCanary } from './upgradeToLatestCanary.js'
import { unzip } from './zip.js'

const startTime = Date.now()
const telemetryInfo: TelemetryInfo = {}

try {
const config = initConfig()
telemetryInfo.template = config.template

if (shouldRelaunch(config)) {
await relaunchOnLatest(config)
Expand All @@ -34,3 +41,5 @@ try {
} catch (e) {
handleError(e)
}

await sendTelemetry(telemetryInfo, Date.now() - startTime)
2 changes: 1 addition & 1 deletion packages/create-redwood-rsc-app/src/installationDir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ async function promptForInstallationDir() {
console.log('The `~username` syntax is not supported here')
console.log(
'Please use the full path or specify the target directory on the ' +
'command line.',
'command line.',
)

return await promptForInstallationDir()
Expand Down
4 changes: 2 additions & 2 deletions packages/create-redwood-rsc-app/src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function printWelcome() {
)
console.log(
'If you need a more customized setup, please use the official installer ' +
'by running `yarn create redwood-app`',
'by running `yarn create redwood-app`',
)
console.log()
}
Expand All @@ -26,7 +26,7 @@ export function printDone(config: Config) {
console.log()
console.log(
'You can now run the following commands to build and serve the included ' +
'example application',
'example application',
)
console.log()
console.log(chalk.hex('cef792')('> cd ' + config.installationDir))
Expand Down
2 changes: 1 addition & 1 deletion packages/create-redwood-rsc-app/src/prerequisites.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export function checkYarnInstallation(config: Config) {
console.log('')
console.log(
'You have more than one active yarn installation. One is installed ' +
"by corepack,\nbut it's not the first one in $PATH.",
"by corepack,\nbut it's not the first one in $PATH.",
)
console.log("Perhaps you've manually installed it using Homebrew or npm.")
console.log(
Expand Down
100 changes: 100 additions & 0 deletions packages/create-redwood-rsc-app/src/telemetry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import ci from 'ci-info'
import fetch from 'node-fetch'

const TELEMETRY_URL =
process.env.REDWOOD_REDIRECT_TELEMETRY ??
'https://telemetry.redwoodjs.com/api/v1/telemetry'

export interface TelemetryInfo {
template?: string
}

// Note: The fields and their names are constrained by the telemetry API
interface TelemetryPayload {
ci: boolean
command: string
complexity: string
duration: number
error?: string
experiments?: string[]
redwoodCi: boolean
system: string
type: 'command'
}

function buildPayload(
telemetryInfo: TelemetryInfo,
duration: number,
): TelemetryPayload {
const command = ['create', 'redwood-rsc-app']
if (process.argv.includes('--no-check-latest')) {
command.push('--no-check-latest')
}

// We don't have a field for the template, so we're using/abusing the experiments field
const experiments: string[] = []
if (telemetryInfo.template) {
experiments.push(`template:${telemetryInfo.template}`)
}

// Detect CI environments
const isCi = ci.isCI
const isRedwoodCi = !!process.env.REDWOOD_CI

// Note: The complexity field is required by the API so we are using a placeholder value
const complexity = '-1.-1.-1.-1.-1'

// Note: The system field is required by the API so we are using a placeholder value
const system = '-1.-1'

return {
ci: isCi,
command: command.join(' '),
complexity,
duration,
experiments,
redwoodCi: isRedwoodCi,
system,
type: 'command',
}
}

export async function sendTelemetry(
telemetryInfo: TelemetryInfo,
duration: number,
) {
if (process.env.REDWOOD_DISABLE_TELEMETRY) {
return
}

try {
const payload = buildPayload(telemetryInfo, duration)

if (process.env.REDWOOD_VERBOSE_TELEMETRY) {
console.info('Redwood Telemetry Payload', payload)
}

const response = await fetch(TELEMETRY_URL, {
method: 'post',
body: JSON.stringify(payload),
headers: { 'Content-Type': 'application/json' },
})

if (process.env.REDWOOD_VERBOSE_TELEMETRY) {
console.info('Redwood Telemetry Response:', response)
}

// Normally we would report on any non-error response here (like a 500)
// but since the process is spawned and stdout/stderr is ignored, it can
// never be seen by the user, so ignore.
if (process.env.REDWOOD_VERBOSE_TELEMETRY && response.status !== 200) {
console.error('Error from telemetry insert:', await response.text())
}
} catch (e) {
// service interruption: network down or telemetry API not responding
// don't let telemetry errors bubble up to user, just do nothing.
if (process.env.REDWOOD_VERBOSE_TELEMETRY) {
console.error('Uncaught error in telemetry:', e)
}
}
}
8 changes: 8 additions & 0 deletions packages/create-redwood-rsc-app/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1421,6 +1421,13 @@ __metadata:
languageName: node
linkType: hard

"ci-info@npm:4.0.0":
version: 4.0.0
resolution: "ci-info@npm:4.0.0"
checksum: 10c0/ecc003e5b60580bd081d83dd61d398ddb8607537f916313e40af4667f9c92a1243bd8e8a591a5aa78e418afec245dbe8e90a0e26e39ca0825129a99b978dd3f9
languageName: node
linkType: hard

"clean-stack@npm:^2.0.0":
version: 2.2.0
resolution: "clean-stack@npm:2.2.0"
Expand Down Expand Up @@ -1532,6 +1539,7 @@ __metadata:
"@types/which": "npm:3.0.4"
"@types/yauzl-promise": "npm:4.0.1"
chalk: "npm:5.3.0"
ci-info: "npm:4.0.0"
concurrently: "npm:^8.2.2"
enquirer: "npm:2.4.1"
esbuild: "npm:0.23.0"
Expand Down

0 comments on commit dfbb132

Please sign in to comment.