diff --git a/lib/events.ts b/lib/events.ts index 2c057e93..5f970164 100644 --- a/lib/events.ts +++ b/lib/events.ts @@ -14,27 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import * as _ from 'lodash'; -import * as Mixpanel from 'mixpanel'; import * as packageJSON from '../package.json'; import { getBalenaSdk } from './utils/lazy'; -const getMixpanel = _.once((balenaUrl: string) => { - return Mixpanel.init('balena-main', { - host: `api.${balenaUrl}`, - path: '/mixpanel', - protocol: 'https', - }); -}); - interface CachedUsername { token: string; username: string; } /** - * Mixpanel.com analytics tracking (information on balena CLI usage). + * Track balena CLI usage events (product improvement analytics). * * @param commandSignature A string like, for example: * "push " @@ -60,7 +50,6 @@ export async function trackCommand(commandSignature: string) { }); } const settings = await import('balena-settings-client'); - const balenaUrl = settings.get('balenaUrl'); const username = await (async () => { const getStorage = await import('balena-settings-storage'); @@ -94,8 +83,6 @@ export async function trackCommand(commandSignature: string) { } })(); - const mixpanel = getMixpanel(balenaUrl); - if (!process.env.BALENARC_NO_SENTRY) { Sentry!.configureScope((scope) => { scope.setUser({ @@ -109,16 +96,43 @@ export async function trackCommand(commandSignature: string) { !process.env.BALENA_CLI_TEST_TYPE && !process.env.BALENARC_NO_ANALYTICS ) { - await mixpanel.track(`[CLI] ${commandSignature}`, { - distinct_id: username, - version: packageJSON.version, - node: process.version, - arch: process.arch, - balenaUrl, // e.g. 'balena-cloud.com' or 'balena-staging.com' - platform: process.platform, - }); + const balenaUrl = settings.get('balenaUrl'); + await sendEvent(balenaUrl, `[CLI] ${commandSignature}`, username); } } catch { // ignore } } + +/** + * Make the event tracking HTTPS request to balenaCloud's '/mixpanel' endpoint. + */ +async function sendEvent(balenaUrl: string, event: string, username?: string) { + const { default: got } = await import('got'); + const trackData = { + event, + properties: { + arch: process.arch, + balenaUrl, // e.g. 'balena-cloud.com' or 'balena-staging.com' + distinct_id: username, + mp_lib: 'node', + node: process.version, + platform: process.platform, + token: 'balena-main', + version: packageJSON.version, + }, + }; + const url = `https://api.${balenaUrl}/mixpanel/track`; + const searchParams = { + ip: 0, + verbose: 0, + data: Buffer.from(JSON.stringify(trackData)).toString('base64'), + }; + try { + await got(url, { searchParams, retry: 0 }); + } catch (e) { + if (process.env.DEBUG) { + console.error(`[debug] Event tracking error: ${e.message || e}`); + } + } +} diff --git a/lib/hooks/prerun/track.ts b/lib/hooks/prerun/track.ts index d5e3afe5..1d584585 100644 --- a/lib/hooks/prerun/track.ts +++ b/lib/hooks/prerun/track.ts @@ -28,7 +28,7 @@ export const trackPromise = new Promise((resolve) => { * parsed by oclif, but before the command's run() function is called. * See: https://oclif.io/docs/hooks * - * This hook is used to track CLI command signatures with mixpanel. + * This hook is used to track CLI command signatures (usage analytics). * A command signature is something like "env add NAME [VALUE]". That's * literally so: 'NAME' and 'VALUE' are NOT replaced with actual values. */ diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 1959aae9..d87ee178 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -6908,19 +6908,6 @@ "es6-symbol": "^3.1.1" } }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "requires": { - "es6-promise": "^4.0.3" - } - }, "es6-symbol": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", @@ -11993,41 +11980,6 @@ } } }, - "mixpanel": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/mixpanel/-/mixpanel-0.10.3.tgz", - "integrity": "sha512-wIYr5o+1XSzJ80o3QED35K/yfPAKi5FigZXTSfcs4vltfeKbilIjNgwxdno7LrqzhjoSjmIyDWkI7D3lr7TwDw==", - "requires": { - "https-proxy-agent": "3.0.0" - }, - "dependencies": { - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "https-proxy-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.0.tgz", - "integrity": "sha512-y4jAxNEihqvBI5F3SaO2rtsjIOnnNA8sEbuiP+UhJZJHeM2NRm6c09ax2tgqme+SgUUvjao2fJXF4h3D6Cb2HQ==", - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - } - } - }, "mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", diff --git a/package.json b/package.json index 538cb033..cb33ee2c 100644 --- a/package.json +++ b/package.json @@ -249,7 +249,6 @@ "livepush": "^3.5.0", "lodash": "^4.17.21", "minimatch": "^3.0.4", - "mixpanel": "^0.10.3", "moment": "^2.27.0", "moment-duration-format": "^2.3.2", "ndjson": "^2.0.0",