Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a telemetry tool integration #146

Merged
merged 2 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to the create-aptos-dapp tool will be captured in this file.

# Unreleased

- Add a telemetry tool integration

# 0.0.13 (2024-07-15)

- Add `dotenv` dev dependency to the `Boilerplate` template
Expand Down
7 changes: 7 additions & 0 deletions src/generateDapp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import fs from "fs/promises";
import type { Ora } from "ora";
import ora from "ora";
import { exec } from "child_process";
// internal files
import { Selections } from "./types.js";
import { copy } from "./utils/helpers.js";
import { recordTelemetry } from "./telemetry.js";

const spinner = (text) => ora({ text, stream: process.stdout });
let currentSpinner: Ora | null = null;
Expand Down Expand Up @@ -122,6 +124,11 @@ export async function generateDapp(selection: Selections) {
const installRootDepsCommand = `npm install --silent --no-progress`;
await runCommand(installRootDepsCommand);

// If approve telemetry
if (selection.telemetry) {
await recordTelemetry(selection);
}

npmSpinner.succeed();
currentSpinner = npmSpinner;

Expand Down
38 changes: 38 additions & 0 deletions src/telemetry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { randomUUID } from "node:crypto";
import { GA4_URL } from "./utils/constants.js";
import { getOS } from "./utils/helpers.js";
import { Selections } from "./types.js";

export const recordTelemetry = async (selection: Selections) => {
try {
const telemetry = {
client_id: randomUUID(), // We generate a random client id for each request
user_id: randomUUID(), // can we find a better way of identifying each CLI user?
timestamp_micros: (Date.now() * 1000).toString(),
events: [
{
name: "wizard_command",
params: {
command: "npx create-aptos-dapp", // TODO make it generic once --example is introduced
project_name: selection.projectName,
template: selection.template.name,
network: selection.network,
os: getOS(),
},
},
],
};
const res = await fetch(GA4_URL, {
method: "POST",
body: JSON.stringify(telemetry),
headers: { "content-type": "application/json" },
});
// this is helpful when using GA4_URL_DEBUG to debug GA4 query. GA4_URL does not return any body response back
if (res.body) {
const resJson = await res.json();
console.log("ga4 debug response", resJson);
}
} catch (err: any) {
console.log("could not record telemetry data", err);
}
};
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export type Selections = {
projectName: string;
template: Template;
network: Network;
telemetry: boolean;
};
5 changes: 5 additions & 0 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// GA4
export const GA_MEASURMENT_ID = "G-QCE2R28N2J";
export const GA_CLIENT_ID = "Xhptd45qQKiPALmqqRheqg";
export const GA4_URL = `https://www.google-analytics.com/mp/collect?measurement_id=${GA_MEASURMENT_ID}&api_secret=${GA_CLIENT_ID}`;
export const GA4_URL_DEBUG = `https://www.google-analytics.com/debug/mp/collect?measurement_id=${GA_MEASURMENT_ID}&api_secret=${GA_CLIENT_ID}`;
18 changes: 18 additions & 0 deletions src/utils/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { execSync } from "child_process";
import path from "path";
import fs from "node:fs";
import os from "node:os";

export const runCommand = (command) => {
try {
Expand All @@ -12,6 +13,7 @@ export const runCommand = (command) => {
return true;
};

// Copy a full source directory into a destination directory
export const copy = (src: string, dest: string) => {
const stat = fs.statSync(src);
if (stat.isDirectory()) {
Expand All @@ -21,6 +23,7 @@ export const copy = (src: string, dest: string) => {
}
};

// Copy a source directory into a destination directory
export const copyDir = (srcDir: string, destDir: string) => {
fs.mkdirSync(destDir, { recursive: true });
for (const file of fs.readdirSync(srcDir)) {
Expand All @@ -30,6 +33,21 @@ export const copyDir = (srcDir: string, destDir: string) => {
}
};

// Get the user OS
export const getOS = () => {
const platform = os.platform();
switch (platform) {
case "darwin":
return "MacOS";
case "linux":
return "Ubuntu";
case "win32":
return "Windows";
default:
return `Unsupported OS ${platform}`;
}
};

/**
* Figures out what package manager to use based on how you ran the command
* E.g. npx, pnpm dlx, yarn dlx...
Expand Down
4 changes: 3 additions & 1 deletion src/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export async function startWorkflow() {
},
workflowOptions.template,
workflowOptions.network,
workflowOptions.analytics,
],
{
onCancel: () => {
Expand Down Expand Up @@ -66,10 +67,11 @@ export async function startWorkflow() {
process.exit(0);
}

const { projectName, template, network } = result;
const { projectName, template, network, telemetry } = result;
return {
projectName,
template,
network,
telemetry,
} as Selections;
}
8 changes: 7 additions & 1 deletion src/workflowOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const workflowOptions = {
type: "select",
name: "network",
message: "Choose your network",
choices(prev, values, prompt) {
choices(prev) {
if (prev.path === "boilerplate-template") {
return [
{ title: "Mainnet", value: "mainnet" },
Expand All @@ -63,4 +63,10 @@ export const workflowOptions = {
initial: 0,
hint: "- You can change this later",
},
analytics: {
type: "confirm",
name: "telemetry",
message: "Help us improve create-aptos-dapp by collection anonymous data",
initial: true,
},
};
Loading