From aa6d526fea010d181f49dd81ae3bdaefb8d1938e Mon Sep 17 00:00:00 2001 From: Edwin Joassart Date: Wed, 7 May 2025 14:57:50 +0200 Subject: [PATCH] patch: remove analytics --- .github/actions/test/action.yml | 2 +- docs/MAINTAINERS.md | 16 +- docs/MANUAL-TESTING.md | 55 +++--- lib/gui/app/app.ts | 23 --- .../drive-selector/drive-selector.tsx | 5 +- lib/gui/app/components/finish/finish.tsx | 2 - .../components/safe-webview/safe-webview.tsx | 5 - lib/gui/app/components/settings/settings.tsx | 2 - .../source-selector/source-selector.tsx | 31 +--- .../target-selector/target-selector.tsx | 14 -- lib/gui/app/i18n/en.ts | 3 +- lib/gui/app/modules/analytics.ts | 92 ---------- lib/gui/app/modules/image-writer.ts | 92 ---------- .../open-external/services/open-external.ts | 3 - lib/gui/app/pages/main/Flash.tsx | 4 +- npm-shrinkwrap.json | 161 ------------------ package.json | 1 - webpack.config.ts | 3 - 18 files changed, 29 insertions(+), 485 deletions(-) diff --git a/.github/actions/test/action.yml b/.github/actions/test/action.yml index 69d7b5a5..c356695b 100644 --- a/.github/actions/test/action.yml +++ b/.github/actions/test/action.yml @@ -12,7 +12,7 @@ inputs: # --- custom environment NODE_VERSION: type: string - default: '20.10' + default: '20.19' VERBOSE: type: string default: 'true' diff --git a/docs/MAINTAINERS.md b/docs/MAINTAINERS.md index 4e91b828..5ef2cd4c 100644 --- a/docs/MAINTAINERS.md +++ b/docs/MAINTAINERS.md @@ -1,10 +1,8 @@ -Maintaining Etcher -================== +# Maintaining Etcher This document is meant to serve as a guide for maintainers to perform common tasks. -Releasing ---------- +## Releasing ### Release Types @@ -13,16 +11,15 @@ Releasing - **release**: Full releases Draft release is created from each PR, tagged with the branch name. -All merged PR will generate a new tag/version as a *pre-release*. +All merged PR will generate a new tag/version as a _pre-release_. Mark the pre-release as final when it is necessary, then distribute the packages in alternative channels as necessary. - #### Preparation - [Prepare the new version](#preparing-a-new-version) - [Generate build artifacts](#generating-binaries) (binaries, archives, etc.) - [Draft a release on GitHub](https://github.com/balena-io/etcher/releases) - - Upload build artifacts to GitHub release draft + - Upload build artifacts to GitHub release draft #### Testing @@ -35,7 +32,7 @@ Mark the pre-release as final when it is necessary, then distribute the packages - [Post release note to forums](https://forums.balena.io/c/etcher) - [Submit Windows binaries to Symantec for whitelisting](#submitting-binaries-to-symantec) - [Update the website](https://github.com/balena-io/etcher-homepage) -- Wait 2-3 hours for analytics (Sentry, Amplitude) to trickle in and check for elevated error rates, or regressions +- Wait 2-3 hours for analytics (Sentry) to trickle in and check for elevated error rates, or regressions - If regressions arise; pull the release, and release a patched version, else: - [Upload deb & rpm packages to Cloudfront](#uploading-packages-to-cloudfront) - Post changelog with `#release-notes` tag on internal chat @@ -51,7 +48,6 @@ Make sure to set the analytics tokens when generating production release binarie ```bash export ANALYTICS_SENTRY_TOKEN="xxxxxx" -export ANALYTICS_AMPLITUDE_TOKEN="xxxxxx" ``` #### Linux @@ -71,7 +67,6 @@ npm run make Our CI will appropriately sign artifacts for macOS and some Windows targets. - ### Uploading packages to Cloudfront Log in to cloudfront and upload the `rpm` and `deb` files. @@ -99,7 +94,6 @@ aws s3api delete-object --bucket --key The Bintray dashboard provides an easy way to delete a version's files. - ### Submitting binaries to Symantec - [Report a Suspected Erroneous Detection](https://submit.symantec.com/false_positive/standard/) diff --git a/docs/MANUAL-TESTING.md b/docs/MANUAL-TESTING.md index 901c51b5..49bdff97 100644 --- a/docs/MANUAL-TESTING.md +++ b/docs/MANUAL-TESTING.md @@ -1,22 +1,19 @@ -Manual Testing -============== +# Manual Testing This document describes a high-level script of manual tests to check for. We should aim to replace items on this list with automated Spectron test cases. -Image Selection ---------------- +## Image Selection - [ ] Cancel image selection dialog - [ ] Select an unbootable image (without a partition table), and expect a - sensible warning + sensible warning - [ ] Attempt to select a ZIP archive with more than one image - [ ] Attempt to select a tar archive (with any compression method) - [ ] Change image selection - [ ] Select a Windows image, and expect a sensible warning -Drive Selection ---------------- +## Drive Selection - [ ] Open the drive selection modal - [ ] Switch drive selection @@ -25,16 +22,15 @@ Drive Selection - [ ] Insert a locked SD Card and expect a warning - [ ] Insert a too small drive and expect a warning - [ ] Put an image into a drive and attempt to flash the image to the drive - that contains it + that contains it - [ ] Attempt to flash a compressed image (for which we can get the - uncompressed size) into a drive that is big enough to hold the compressed - image, but not big enough to hold the uncompressed version + uncompressed size) into a drive that is big enough to hold the compressed + image, but not big enough to hold the uncompressed version - [ ] Enable "Unsafe Mode" and attempt to select a system drive - [ ] Enable "Unsafe Mode", and if there is only one system drive (and no - removable ones), don't expect autoselection + removable ones), don't expect autoselection -Image Support -------------- +## Image Support Run the following tests with and without validation enabled: @@ -51,18 +47,17 @@ Run the following tests with and without validation enabled: - [ ] Flash an archive image containing a blockmap file - [ ] Flash an archive image containing a manifest metadata file -Flashing Process ----------------- +## Flashing Process - [ ] Unplug the drive during flash or validation - [ ] Click "Flash", cancel elevation dialog, and click "Flash" again - [ ] Start flashing an image, try to close Etcher, cancel the application - close warning dialog, and check that Etcher continues to flash the image + close warning dialog, and check that Etcher continues to flash the image ### Child Writer - [ ] Kill the child writer process (i.e. with `SIGINT` or `SIGKILL`), and - check that the UI reacts appropriately + check that the UI reacts appropriately - [ ] Close the application while flashing using the window manager close icon - [ ] Close the application while flashing using the OS keyboard shortcut - [ ] Close the application from the terminal using Ctrl-C while flashing @@ -72,11 +67,10 @@ In all these cases, the child writer process should not remain alive. Note that in some systems you need to open your process monitor tool of choice with extra permissions to see the elevated child writer process. -GUI ----- +## GUI - [ ] Close application from the terminal using Ctrl-C while the application is - idle + idle - [ ] Click footer links that take you to an external website - [ ] Attempt to change image or drive selection while flashing - [ ] Go to the settings page while flashing and come back @@ -85,31 +79,20 @@ GUI - [ ] Minimize the application - [ ] Start the application given no internet connection -Success Banner --------------- +## Success Banner - [ ] Click an external link on the success banner (with and without internet - connection) + connection) -Elevation Prompt ----------------- +## Elevation Prompt - [ ] Flash an image as `root`/administrator - [ ] Reject elevation prompt - [ ] Put incorrect elevation prompt password - [ ] Unplug the drive during elevation -Unmounting ----------- +## Unmounting - [ ] Disable unmounting and flash an image - [ ] Flash an image with a file system that is readable by the host OS, and - check that is unmounted correctly - -Analytics ---------- - -- [ ] Disable analytics, open DevTools Network pane or a packet sniffer, and - check that no request is sent -- [ ] **Disable analytics, refresh application from DevTools (using Cmd-R or - F5), and check that initial events are not sent to Amplitude** + check that is unmounted correctly diff --git a/lib/gui/app/app.ts b/lib/gui/app/app.ts index 5457909c..f4ea7908 100644 --- a/lib/gui/app/app.ts +++ b/lib/gui/app/app.ts @@ -64,9 +64,6 @@ store.dispatch({ data: uuidV4(), }); -const applicationSessionUuid = store.getState().toJS().applicationSessionUuid; -const flashingWorkflowUuid = store.getState().toJS().flashingWorkflowUuid; - console.log(outdent` ${outdent} _____ _ _ @@ -82,13 +79,6 @@ console.log(outdent` Version = ${packageJSON.version}, Type = ${packageJSON.packageType} `); -const currentVersion = packageJSON.version; - -analytics.logEvent('Application start', { - packageType: packageJSON.packageType, - version: currentVersion, -}); - const debouncedLog = debounce(console.log, 1000, { maxWait: 1000 }); function pluralize(word: string, quantity: number) { @@ -172,9 +162,6 @@ analytics.initAnalytics(); window.addEventListener('beforeunload', async (event) => { if (!flashState.isFlashing() || popupExists) { - analytics.logEvent('Close application', { - isFlashing: flashState.isFlashing(), - }); return; } @@ -184,8 +171,6 @@ window.addEventListener('beforeunload', async (event) => { // Don't open any more popups popupExists = true; - analytics.logEvent('Close attempt while flashing'); - try { const confirmed = await osDialog.showWarning({ confirmationLabel: i18next.t('yesExit'), @@ -194,19 +179,11 @@ window.addEventListener('beforeunload', async (event) => { description: messages.warning.exitWhileFlashing(), }); if (confirmed) { - analytics.logEvent('Close confirmed while flashing', { - flashInstanceUuid: flashState.getFlashUuid(), - }); - // This circumvents the 'beforeunload' event unlike // remote.app.quit() which does not. remote.process.exit(EXIT_CODES.SUCCESS); } - analytics.logEvent('Close rejected while flashing', { - applicationSessionUuid, - flashingWorkflowUuid, - }); popupExists = false; } catch (error: any) { exceptionReporter.report(error); diff --git a/lib/gui/app/components/drive-selector/drive-selector.tsx b/lib/gui/app/components/drive-selector/drive-selector.tsx index d3c2d389..bc65ebba 100644 --- a/lib/gui/app/components/drive-selector/drive-selector.tsx +++ b/lib/gui/app/components/drive-selector/drive-selector.tsx @@ -36,7 +36,7 @@ import prettyBytes from 'pretty-bytes'; import { getDrives, hasAvailableDrives } from '../../models/available-drives'; import { getImage, isDriveSelected } from '../../models/selection-state'; import { store } from '../../models/store'; -import { logEvent, logException } from '../../modules/analytics'; +import { logException } from '../../modules/analytics'; import { open as openExternal } from '../../os/open-external/services/open-external'; import type { GenericTableProps } from '../../styled-components'; import { Alert, Modal, Table } from '../../styled-components'; @@ -355,9 +355,6 @@ export class DriveSelector extends React.Component< private installMissingDrivers(drive: DriverlessDrive) { if (drive.link) { - logEvent('Open driver link modal', { - url: drive.link, - }); this.setState({ missingDriversModal: { drive } }); } } diff --git a/lib/gui/app/components/finish/finish.tsx b/lib/gui/app/components/finish/finish.tsx index 223cb242..414da88a 100644 --- a/lib/gui/app/components/finish/finish.tsx +++ b/lib/gui/app/components/finish/finish.tsx @@ -22,7 +22,6 @@ import * as flashState from '../../models/flash-state'; import * as selectionState from '../../models/selection-state'; import * as settings from '../../models/settings'; import { Actions, store } from '../../models/store'; -import * as analytics from '../../modules/analytics'; import { FlashAnother } from '../flash-another/flash-another'; import type { FlashError } from '../flash-results/flash-results'; import { FlashResults } from '../flash-results/flash-results'; @@ -30,7 +29,6 @@ import { SafeWebview } from '../safe-webview/safe-webview'; function restart(goToMain: () => void) { selectionState.deselectAllDrives(); - analytics.logEvent('Restart'); // Reset the flashing workflow uuid store.dispatch({ diff --git a/lib/gui/app/components/safe-webview/safe-webview.tsx b/lib/gui/app/components/safe-webview/safe-webview.tsx index 62b08d78..efe8ae3d 100644 --- a/lib/gui/app/components/safe-webview/safe-webview.tsx +++ b/lib/gui/app/components/safe-webview/safe-webview.tsx @@ -21,7 +21,6 @@ import * as React from 'react'; import * as packageJSON from '../../../../../package.json'; import * as settings from '../../models/settings'; -import * as analytics from '../../modules/analytics'; /** * @summary Electron session identifier @@ -196,10 +195,6 @@ export class SafeWebview extends React.PureComponent< // only care about this event if it's a request for the main frame if (event.resourceType === 'mainFrame') { const HTTP_OK = 200; - const { webContents, ...webviewEvent } = event; - analytics.logEvent('SafeWebview loaded', { - ...webviewEvent, - }); this.setState({ shouldShow: event.statusCode === HTTP_OK, }); diff --git a/lib/gui/app/components/settings/settings.tsx b/lib/gui/app/components/settings/settings.tsx index 4c8129c8..50d73f84 100644 --- a/lib/gui/app/components/settings/settings.tsx +++ b/lib/gui/app/components/settings/settings.tsx @@ -21,7 +21,6 @@ import { Box, Checkbox, Flex, Txt } from 'rendition'; import { version, packageType } from '../../../../../package.json'; import * as settings from '../../models/settings'; -import * as analytics from '../../modules/analytics'; import { open as openExternal } from '../../os/open-external/services/open-external'; import { Modal } from '../../styled-components'; import * as i18next from 'i18next'; @@ -89,7 +88,6 @@ export function SettingsModal({ toggleModal }: SettingsModalProps) { const toggleSetting = async (setting: string) => { const value = currentSettings[setting]; - analytics.logEvent('Toggle setting', { setting, value }); await settings.set(setting, !value); setCurrentSettings({ ...currentSettings, diff --git a/lib/gui/app/components/source-selector/source-selector.tsx b/lib/gui/app/components/source-selector/source-selector.tsx index 82808f8d..00e7cf03 100644 --- a/lib/gui/app/components/source-selector/source-selector.tsx +++ b/lib/gui/app/components/source-selector/source-selector.tsx @@ -392,10 +392,6 @@ export class SourceSelector extends React.Component< } private reselectSource() { - analytics.logEvent('Reselect image', { - previousImage: selectionState.getImage(), - }); - selectionState.deselectImage(); this.props.hideAnalyticsAlert(); } @@ -426,7 +422,6 @@ export class SourceSelector extends React.Component< } if (supportedFormats.looksLikeWindowsImage(selected)) { - analytics.logEvent('Possibly Windows image', { image: selected }); this.setState({ warning: { message: messages.warning.looksLikeWindowsImage(), @@ -450,7 +445,6 @@ export class SourceSelector extends React.Component< metadata = await requestMetadata({ selected, SourceType, auth }); if (!metadata?.hasMBR && this.state.warning === null) { - analytics.logEvent('Missing partition table', { metadata }); this.setState({ warning: { message: messages.warning.missingPartitionTable(), @@ -468,7 +462,6 @@ export class SourceSelector extends React.Component< } } else { if (selected.partitionTableType === null) { - analytics.logEvent('Missing partition table', { selected }); this.setState({ warning: { message: messages.warning.driveMissingPartitionTable(), @@ -490,15 +483,6 @@ export class SourceSelector extends React.Component< metadata.auth = auth; metadata.SourceType = SourceType; selectionState.selectSource(metadata); - analytics.logEvent('Select image', { - // An easy way so we can quickly identify if we're making use of - // certain features without printing pages of text to DevTools. - image: { - ...metadata, - logo: Boolean(metadata.logo), - blockMap: Boolean(metadata.blockMap), - }, - }); } })(), }; @@ -519,11 +503,9 @@ export class SourceSelector extends React.Component< analytics.logException(error); return; } - analytics.logEvent(title, { path: sourcePath }); } private async openImageSelector() { - analytics.logEvent('Open image selector'); this.setState({ imageSelectorOpen: true }); try { @@ -531,7 +513,6 @@ export class SourceSelector extends React.Component< // Avoid analytics and selection state changes // if no file was resolved from the dialog. if (!imagePath) { - analytics.logEvent('Image selector closed'); return; } await this.selectSource(imagePath, 'File').promise; @@ -550,16 +531,12 @@ export class SourceSelector extends React.Component< } private openURLSelector() { - analytics.logEvent('Open image URL selector'); - this.setState({ showURLSelector: true, }); } private openDriveSelector() { - analytics.logEvent('Open drive selector'); - this.setState({ showDriveSelector: true, }); @@ -576,10 +553,6 @@ export class SourceSelector extends React.Component< } private showSelectedImageDetails() { - analytics.logEvent('Show selected image tooltip', { - imagePath: selectionState.getImage()?.path, - }); - this.setState({ showImageDetails: true, }); @@ -759,9 +732,7 @@ export class SourceSelector extends React.Component< done={async (imageURL: string, auth?: Authentication) => { // Avoid analytics and selection state changes // if no file was resolved from the dialog. - if (!imageURL) { - analytics.logEvent('URL selector closed'); - } else { + if (imageURL) { let promise; ({ promise, cancel: cancelURLSelection } = this.selectSource( imageURL, diff --git a/lib/gui/app/components/target-selector/target-selector.tsx b/lib/gui/app/components/target-selector/target-selector.tsx index a79daed9..8a7f867f 100644 --- a/lib/gui/app/components/target-selector/target-selector.tsx +++ b/lib/gui/app/components/target-selector/target-selector.tsx @@ -20,7 +20,6 @@ import { Flex, Txt } from 'rendition'; import type { DriveSelectorProps } from '../drive-selector/drive-selector'; import { DriveSelector } from '../drive-selector/drive-selector'; import { - isDriveSelected, getImage, getSelectedDrives, deselectDrive, @@ -28,7 +27,6 @@ import { deselectAllDrives, } from '../../models/selection-state'; import { observe } from '../../models/store'; -import * as analytics from '../../modules/analytics'; import { TargetSelectorButton } from './target-selector-button'; import TgtSvg from '../../../assets/tgt.svg'; @@ -77,21 +75,10 @@ export const selectAllTargets = (modalTargets: DrivelistDrive[]) => { ); // deselect drives deselected.forEach((drive) => { - analytics.logEvent('Toggle drive', { - drive, - previouslySelected: true, - }); deselectDrive(drive.device); }); // select drives modalTargets.forEach((drive) => { - // Don't send events for drives that were already selected - if (!isDriveSelected(drive.device)) { - analytics.logEvent('Toggle drive', { - drive, - previouslySelected: false, - }); - } selectDrive(drive.device); }); }; @@ -142,7 +129,6 @@ export const TargetSelector = ({ hideAnalyticsAlert(); }} reselectDrive={() => { - analytics.logEvent('Reselect drive'); setShowTargetSelectorModal(true); }} flashing={flashing} diff --git a/lib/gui/app/i18n/en.ts b/lib/gui/app/i18n/en.ts index 4d6fd34f..3e7c3fd8 100644 --- a/lib/gui/app/i18n/en.ts +++ b/lib/gui/app/i18n/en.ts @@ -133,8 +133,7 @@ const translation = { flashCompleted: 'Flash Completed!', }, settings: { - errorReporting: - 'Anonymously report errors and usage statistics to balena.io', + errorReporting: 'Anonymously report errors to balena.io', autoUpdate: 'Auto-updates enabled', settings: 'Settings', systemInformation: 'System Information', diff --git a/lib/gui/app/modules/analytics.ts b/lib/gui/app/modules/analytics.ts index 4ccc4d67..5452e192 100644 --- a/lib/gui/app/modules/analytics.ts +++ b/lib/gui/app/modules/analytics.ts @@ -15,12 +15,8 @@ */ import { findLastIndex, once } from 'lodash'; -import type { Client } from 'analytics-client'; -import { createClient, createNoopClient } from 'analytics-client'; import * as SentryRenderer from '@sentry/electron/renderer'; import * as settings from '../models/settings'; -import { store } from '../models/store'; -import { version } from '../../../../package.json'; type AnalyticsPayload = _.Dictionary; @@ -115,7 +111,6 @@ export const anonymizeAnalyticsPayload = ( return data; }; -let analyticsClient: Client; /** * @summary Init analytics configurations */ @@ -127,95 +122,8 @@ export const initAnalytics = once(() => { beforeSend: anonymizeSentryData, debug: process.env.ETCHER_SENTRY_DEBUG === 'true', }); - - const projectName = - settings.getSync('analyticsAmplitudeToken') || process.env.AMPLITUDE_TOKEN; - - const clientConfig = { - projectName, - endpoint: 'data.balena-cloud.com', - componentName: 'etcher', - componentVersion: version, - }; - analyticsClient = projectName - ? createClient(clientConfig) - : createNoopClient(); }); -const getCircularReplacer = () => { - const seen = new WeakSet(); - return (_key: any, value: any) => { - if (typeof value === 'object' && value !== null) { - if (seen.has(value)) { - return; - } - seen.add(value); - } - return value; - }; -}; - -function flattenObject(obj: any) { - const toReturn: AnalyticsPayload = {}; - - for (const i in obj) { - if (!Object.prototype.hasOwnProperty.call(obj, i)) { - continue; - } - - if (Array.isArray(obj[i])) { - toReturn[i] = obj[i]; - continue; - } - - if (typeof obj[i] === 'object' && obj[i] !== null) { - const flatObject = flattenObject(obj[i]); - for (const x in flatObject) { - if (!Object.prototype.hasOwnProperty.call(flatObject, x)) { - continue; - } - - toReturn[i.toLowerCase() + '.' + x.toLowerCase()] = flatObject[x]; - } - } else { - toReturn[i] = obj[i]; - } - } - return toReturn; -} - -function formatEvent(data: any): AnalyticsPayload { - const event = JSON.parse(JSON.stringify(data, getCircularReplacer())); - return anonymizeAnalyticsPayload(flattenObject(event)); -} - -function reportAnalytics(message: string, data: AnalyticsPayload = {}) { - const { applicationSessionUuid, flashingWorkflowUuid } = store - .getState() - .toJS(); - - const event = formatEvent({ - ...data, - applicationSessionUuid, - flashingWorkflowUuid, - }); - analyticsClient.track(message, event); -} - -/** - * @summary Log an event - * - * @description - * This function sends the debug message to product analytics services. - */ -export async function logEvent(message: string, data: AnalyticsPayload = {}) { - const shouldReportAnalytics = await settings.get('errorReporting'); - if (shouldReportAnalytics) { - initAnalytics(); - reportAnalytics(message, data); - } -} - /** * @summary Log an exception * diff --git a/lib/gui/app/modules/image-writer.ts b/lib/gui/app/modules/image-writer.ts index d1701e75..8bcadc5f 100644 --- a/lib/gui/app/modules/image-writer.ts +++ b/lib/gui/app/modules/image-writer.ts @@ -20,44 +20,11 @@ import type { Dictionary } from 'lodash'; import * as errors from '../../../shared/errors'; import type { SourceMetadata } from '../../../shared/typings/source-selector'; import * as flashState from '../models/flash-state'; -import * as selectionState from '../models/selection-state'; import * as settings from '../models/settings'; -import * as analytics from '../modules/analytics'; import * as windowProgress from '../os/window-progress'; import { spawnChildAndConnect } from './api'; -/** - * @summary Handle a flash error and log it to analytics - */ -function handleErrorLogging( - error: Error & { code: string }, - analyticsData: any, -) { - const eventData = { - ...analyticsData, - flashInstanceUuid: flashState.getFlashUuid(), - }; - - if (error.code === 'EVALIDATION') { - analytics.logEvent('Validation error', eventData); - } else if (error.code === 'EUNPLUGGED') { - analytics.logEvent('Drive unplugged', eventData); - } else if (error.code === 'EIO') { - analytics.logEvent('Input/output error', eventData); - } else if (error.code === 'ENOSPC') { - analytics.logEvent('Out of space', eventData); - } else if (error.code === 'ECHILDDIED') { - analytics.logEvent('Child died unexpectedly', eventData); - } else { - analytics.logEvent('Flash error', { - ...eventData, - error: errors.toJSON(error), - }); - } -} - let cancelEmitter: (type: string) => void | undefined; - interface FlashResults { skip?: boolean; cancelled?: boolean; @@ -88,14 +55,6 @@ async function performWrite( const flashResults: FlashResults = {}; - const analyticsData = { - image, - drives, - driveCount: drives.length, - uuid: flashState.getFlashUuid(), - flashInstanceUuid: flashState.getFlashUuid(), - }; - const onFail = ({ device, error }: { device: any; error: any }) => { console.log('fail event'); console.log(device); @@ -103,7 +62,6 @@ async function performWrite( if (device.devicePath) { flashState.addFailedDeviceError({ device, error }); } - handleErrorLogging(error, analyticsData); finish(); }; @@ -195,17 +153,6 @@ export async function flash( drives.map((d) => d.devicePath).filter((p) => p != null) as string[], ); - const analyticsData = { - image, - drives, - driveCount: drives.length, - uuid: flashState.getFlashUuid(), - status: 'started', - flashInstanceUuid: flashState.getFlashUuid(), - }; - - analytics.logEvent('Flash', analyticsData); - // start api and call the flasher try { const result = await write(image, drives, flashState.setProgressState); @@ -220,39 +167,10 @@ export async function flash( windowProgress.clear(); - const { results = {} } = flashState.getFlashResults(); - - const eventData = { - ...analyticsData, - errors: results.errors, - devices: results.devices, - status: 'failed', - error, - }; - analytics.logEvent('Write failed', eventData); throw error; } windowProgress.clear(); - - if (flashState.wasLastFlashCancelled()) { - const eventData = { - ...analyticsData, - status: 'cancel', - }; - analytics.logEvent('Elevation cancelled', eventData); - } else { - const { results = {} } = flashState.getFlashResults(); - const eventData = { - ...analyticsData, - errors: results.errors, - devices: results.devices, - status: 'finished', - bytesWritten: results.bytesWritten, - sourceMetadata: results.sourceMetadata, - }; - analytics.logEvent('Done', eventData); - } } /** @@ -261,16 +179,6 @@ export async function flash( */ export async function cancel(type: string) { const status = type.toLowerCase(); - const drives = selectionState.getSelectedDevices(); - const analyticsData = { - image: selectionState.getImage()?.path, - drives, - driveCount: drives.length, - uuid: flashState.getFlashUuid(), - flashInstanceUuid: flashState.getFlashUuid(), - status, - }; - analytics.logEvent('Cancel', analyticsData); if (cancelEmitter) { cancelEmitter(status); diff --git a/lib/gui/app/os/open-external/services/open-external.ts b/lib/gui/app/os/open-external/services/open-external.ts index ef7c99c5..e9afb365 100644 --- a/lib/gui/app/os/open-external/services/open-external.ts +++ b/lib/gui/app/os/open-external/services/open-external.ts @@ -16,7 +16,6 @@ import * as electron from 'electron'; import * as settings from '../../../models/settings'; -import { logEvent } from '../../../modules/analytics'; /** * @summary Open an external resource @@ -27,8 +26,6 @@ export async function open(url: string) { return; } - logEvent('Open external link', { url }); - if (url) { electron.shell.openExternal(url); } diff --git a/lib/gui/app/pages/main/Flash.tsx b/lib/gui/app/pages/main/Flash.tsx index 25aa4260..0042f623 100644 --- a/lib/gui/app/pages/main/Flash.tsx +++ b/lib/gui/app/pages/main/Flash.tsx @@ -198,9 +198,7 @@ export class FlashStep extends React.PureComponent< private handleFlashErrorResponse(shouldRetry: boolean) { this.setState({ errorMessage: '' }); flashState.resetState(); - if (shouldRetry) { - analytics.logEvent('Restart after failure'); - } else { + if (!shouldRetry) { selection.clear(); } } diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index af06763d..d7cdf0e0 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -13,7 +13,6 @@ "@fortawesome/fontawesome-free": "^6.5.2", "@ronomon/direct-io": "^3.0.1", "@sentry/electron": "^4.24.0", - "analytics-client": "^2.0.1", "axios": "^1.6.8", "debug": "4.3.4", "drivelist": "^12.0.2", @@ -101,148 +100,6 @@ "node": ">=0.10.0" } }, - "node_modules/@amplitude/analytics-browser": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-browser/-/analytics-browser-1.13.4.tgz", - "integrity": "sha512-FyNlrhLZUFI+lDHxbDGMoZED80iARS6VjTP+zZcfk0GWI7+lt0Meu5jD7G8xms21Ioxrg1+Lf1Q4v3CC6XN2Nw==", - "dependencies": { - "@amplitude/analytics-client-common": "^1.2.2", - "@amplitude/analytics-core": "^1.2.5", - "@amplitude/analytics-types": "^1.3.4", - "@amplitude/plugin-page-view-tracking-browser": "^1.0.12", - "@amplitude/plugin-web-attribution-browser": "^1.0.12", - "@amplitude/ua-parser-js": "^0.7.31", - "tslib": "^2.4.1" - } - }, - "node_modules/@amplitude/analytics-client-common": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-1.2.2.tgz", - "integrity": "sha512-vwGgVXl9FKEi99OzjqqhX8RrulQQ55aAllhgbdyxpyyAQ5NbbZOPdrxp1ow0oliCVvbSDgUYOAeAwTChIgnStA==", - "dependencies": { - "@amplitude/analytics-connector": "^1.5.0", - "@amplitude/analytics-core": "^1.2.5", - "@amplitude/analytics-types": "^1.3.4", - "tslib": "^2.4.1" - } - }, - "node_modules/@amplitude/analytics-connector": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-connector/-/analytics-connector-1.5.0.tgz", - "integrity": "sha512-T8mOYzB9RRxckzhL0NTHwdge9xuFxXEOplC8B1Y3UX3NHa3BLh7DlBUZlCOwQgMc2nxDfnSweDL5S3bhC+W90g==" - }, - "node_modules/@amplitude/analytics-core": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-1.2.5.tgz", - "integrity": "sha512-V7CVlHVN+1diKiOpdp2bCPZ0mbS4CmUYF+v+eXDwVfJL3M/t3sVcT1apXnmVYGYi14cGu9hQOD11rD6qKbUOsw==", - "dependencies": { - "@amplitude/analytics-types": "^1.3.4", - "tslib": "^2.4.1" - } - }, - "node_modules/@amplitude/analytics-types": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-1.3.4.tgz", - "integrity": "sha512-tR70gzqFkEzX9QpxvWYMfLCledT7vMhgd3d4/bkp3nnGXTOORaVUOCcSgOyxyuFdSx84T61aP/eZPKIcZcaP+A==" - }, - "node_modules/@amplitude/marketing-analytics-browser": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@amplitude/marketing-analytics-browser/-/marketing-analytics-browser-0.2.9.tgz", - "integrity": "sha512-xOx5tCqV2A1r9+pYP7PPDfBqzQpKvhmIPR/CF4blpo7ZTYqCIWLg7QG1pN3uWFQuq5d4MWWz5QH+TUvKXifsHA==", - "dependencies": { - "@amplitude/analytics-browser": "^1.6.3", - "@amplitude/analytics-client-common": "^0.4.1", - "@amplitude/analytics-core": "^0.10.1", - "@amplitude/analytics-types": "^0.13.0", - "@amplitude/plugin-page-view-tracking-browser": "^0.4.9", - "@amplitude/plugin-web-attribution-browser": "^0.4.2", - "tslib": "^2.3.1" - } - }, - "node_modules/@amplitude/marketing-analytics-browser/node_modules/@amplitude/analytics-client-common": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-client-common/-/analytics-client-common-0.4.1.tgz", - "integrity": "sha512-cwKHZVNfBt8kNmhXuSZ/BkEwdOSsCVQDXKgQysb4sp5AYkwqYV/bVd7yvWxffrrkK4N2PsYLnvODeTwANH/4UQ==", - "dependencies": { - "@amplitude/analytics-connector": "^1.4.5", - "@amplitude/analytics-core": "^0.10.1", - "@amplitude/analytics-types": "^0.13.0", - "tslib": "^2.3.1" - } - }, - "node_modules/@amplitude/marketing-analytics-browser/node_modules/@amplitude/analytics-core": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-core/-/analytics-core-0.10.1.tgz", - "integrity": "sha512-XYJavGCnf0Y28chswEGNjSM2MqCMafsQvHpSgRD1JYTNrv+j/CTkj7P3TwyxriaXCTSfFcWTDPKRHd3SruU3Aw==", - "dependencies": { - "@amplitude/analytics-types": "^0.13.0", - "tslib": "^2.3.1" - } - }, - "node_modules/@amplitude/marketing-analytics-browser/node_modules/@amplitude/analytics-types": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@amplitude/analytics-types/-/analytics-types-0.13.0.tgz", - "integrity": "sha512-yti2SytTIh0R5QknuKO1RMgB+r8CGjauhPfFaaYiTm4keAvqYxDdG9ULarPDoOx2VPSfB5Za779Kt1Muc+34PA==" - }, - "node_modules/@amplitude/marketing-analytics-browser/node_modules/@amplitude/plugin-page-view-tracking-browser": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-0.4.9.tgz", - "integrity": "sha512-dPeMativPA+UitDQbRv/FtBfAZtddbu9tgezbtSR90yubK+bS3oYWXETxR6X7P73qNBIZGUpH9i2nWVY6EXVQQ==", - "dependencies": { - "@amplitude/analytics-client-common": "^0.4.1", - "@amplitude/analytics-types": "^0.13.0", - "tslib": "^2.3.1" - } - }, - "node_modules/@amplitude/marketing-analytics-browser/node_modules/@amplitude/plugin-web-attribution-browser": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-attribution-browser/-/plugin-web-attribution-browser-0.4.2.tgz", - "integrity": "sha512-9N1Qe4fTBmS7uDcgCA5PbIryJCf2V+BUhwP8n6BSwH1XOujx/sK0UQRgPlGRFSmmm2eH7QG9RKJkQj6VB3/zrQ==", - "dependencies": { - "@amplitude/analytics-client-common": "^0.4.1", - "@amplitude/analytics-types": "^0.13.0", - "tslib": "^2.3.1" - } - }, - "node_modules/@amplitude/plugin-page-view-tracking-browser": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-page-view-tracking-browser/-/plugin-page-view-tracking-browser-1.0.12.tgz", - "integrity": "sha512-zbFbBi/+QrsWm1rPcFIAcxQ3t7uZwTuHCnHHzyZU/nQB/gyOgRh4U4uqt5DekLf4Tp3V2a+hmhmTE0KfRFuXLw==", - "dependencies": { - "@amplitude/analytics-client-common": "^1.2.2", - "@amplitude/analytics-types": "^1.3.4", - "tslib": "^2.4.1" - } - }, - "node_modules/@amplitude/plugin-web-attribution-browser": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@amplitude/plugin-web-attribution-browser/-/plugin-web-attribution-browser-1.0.12.tgz", - "integrity": "sha512-zoIqgIT34xbE3V2TyQYoRVCs7j3biY/AXkGzYphiriuUvKmQtRjBoP2o08nZHYFzVSOSq4Ixk7OlelilW10Krg==", - "dependencies": { - "@amplitude/analytics-client-common": "^1.2.2", - "@amplitude/analytics-core": "^1.2.5", - "@amplitude/analytics-types": "^1.3.4", - "tslib": "^2.4.1" - } - }, - "node_modules/@amplitude/ua-parser-js": { - "version": "0.7.33", - "resolved": "https://registry.npmjs.org/@amplitude/ua-parser-js/-/ua-parser-js-0.7.33.tgz", - "integrity": "sha512-wKEtVR4vXuPT9cVEIJkYWnlF++Gx3BdLatPBM+SZ1ztVIvnhdGBZR/mn9x/PzyrMcRlZmyi6L56I2J3doVBnjA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - } - ], - "engines": { - "node": "*" - } - }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -7026,16 +6883,6 @@ "ajv": "^6.9.1" } }, - "node_modules/analytics-client": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/analytics-client/-/analytics-client-2.0.2.tgz", - "integrity": "sha512-03Qo4r86wzw7NV0voG7xNwZjbba7h0wC6A8Dd85Slgt1bMg0jWKBXS9DnWIMiUMT4vfm8HtLaBDJyJIKlu1glQ==", - "dependencies": { - "@amplitude/analytics-browser": "^1.5.4", - "@amplitude/marketing-analytics-browser": "^0.2.4", - "js-cookie": "^3.0.1" - } - }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -16848,14 +16695,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/js-cookie": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", - "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", - "engines": { - "node": ">=14" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/package.json b/package.json index e5eedc49..7c4036db 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,6 @@ "@fortawesome/fontawesome-free": "^6.5.2", "@ronomon/direct-io": "^3.0.1", "@sentry/electron": "^4.24.0", - "analytics-client": "^2.0.1", "axios": "^1.6.8", "debug": "4.3.4", "drivelist": "^12.0.2", diff --git a/webpack.config.ts b/webpack.config.ts index 998db800..bbe880a5 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -63,9 +63,6 @@ const rules: Required['rules'] = [ const injectAnalyticsToken = new DefinePlugin({ 'process.env.SENTRY_TOKEN': JSON.stringify(process.env.SENTRY_TOKEN || ''), - 'process.env.AMPLITUDE_TOKEN': JSON.stringify( - process.env.AMPLITUDE_TOKEN || '', - ), }); export const rendererConfig: Configuration = {