Skip to content

Commit

Permalink
refactor: Track cdn download for budget
Browse files Browse the repository at this point in the history
  • Loading branch information
ci010 committed Dec 3, 2024
1 parent 2ec212b commit 6262c73
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 41 deletions.
78 changes: 37 additions & 41 deletions xmcl-electron-app/main/utils/updater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Logger } from '@xmcl/runtime/logger'
import { AbortableTask, BaseTask, Task, task } from '@xmcl/task'
import { spawn } from 'child_process'
import { shell } from 'electron'
import { AppUpdater, CancellationToken, Provider, UpdateInfo, UpdaterSignal, autoUpdater } from 'electron-updater'
import updater, { AppUpdater, CancellationToken, UpdaterSignal } from 'electron-updater'
import { createWriteStream } from 'fs'
import { readFile, writeFile } from 'fs-extra'
import { closeSync, existsSync, open, rename, unlink } from 'original-fs'
Expand All @@ -16,16 +16,14 @@ import { PassThrough, Readable } from 'stream'
import { pipeline } from 'stream/promises'
import { promisify } from 'util'
import { createGunzip } from 'zlib'
import { kGFW } from '~/gfw'
import { AnyError, isSystemError } from '~/util/error'
import { checksum } from '~/util/fs'
import ElectronLauncherApp from '../ElectronLauncherApp'
import { DownloadAppInstallerTask } from './appinstaller'
import { ensureElevateExe } from './elevate'
import { kGFW } from '~/gfw'

const kPatched = Symbol('Patched')
// @ts-ignore
const getUpdateInfoAndProvider = AppUpdater.prototype.getUpdateInfoAndProvider

/**
* Only download asar file update.
Expand Down Expand Up @@ -76,6 +74,9 @@ export class DownloadAsarUpdateTask extends AbortableTask<void> {
return
}
const gzUrl = url + '.gz'
if (url.startsWith(AZURE_CDN)) {
this.app.emit('download-cdn', 'asar', this.file)
}
const gzResponse = await fetch(gzUrl, { signal: this.abortController.signal })
const tracker = new PassThrough({
transform: (chunk, encoding, callback) => {
Expand Down Expand Up @@ -158,18 +159,44 @@ async function getUpdateAsarViaBatArgs(appAsarPath: string, updateAsarPath: stri
* Download the full update. This size can be larger as it carry the whole electron thing...
*/
export class DownloadFullUpdateTask extends AbortableTask<void> {
private updateSignal = new UpdaterSignal(autoUpdater)
constructor(private app: ElectronLauncherApp, private appUpdater: AppUpdater) {
super()
}

private cancellationToken = new CancellationToken()

protected async process(): Promise<void> {
this.cancellationToken = new CancellationToken()
this.updateSignal.progress((info) => {

const gfw = await this.app.registry.get(kGFW)

Check failure on line 172 in xmcl-electron-app/main/utils/updater.ts

View workflow job for this annotation

GitHub Actions / validate

Trailing spaces not allowed
if (gfw.inside) {
// @ts-ignore
const executor = autoUpdater.httpExecutor as any

Check failure on line 175 in xmcl-electron-app/main/utils/updater.ts

View workflow job for this annotation

GitHub Actions / validate

'autoUpdater' is not defined
if (!(kPatched in executor)) {
const createRequest = executor.createRequest.bind(executor)
Object.assign(executor, {
[kPatched]: true,
createRequest: (options: any, callback: any) => {
if (gfw.inside) {
const url = new URL(AZURE_CDN)
options.hostname = url.hostname;

Check failure on line 183 in xmcl-electron-app/main/utils/updater.ts

View workflow job for this annotation

GitHub Actions / validate

Extra semicolon
options.path = `/releases/${basename(options.pathname)}`;

Check failure on line 184 in xmcl-electron-app/main/utils/updater.ts

View workflow job for this annotation

GitHub Actions / validate

Extra semicolon
this.app.emit('download-cdn', 'electron', basename(options.pathname))
}
return createRequest(options, callback)
}

Check failure on line 188 in xmcl-electron-app/main/utils/updater.ts

View workflow job for this annotation

GitHub Actions / validate

Missing trailing comma
})
}
}

Check failure on line 192 in xmcl-electron-app/main/utils/updater.ts

View workflow job for this annotation

GitHub Actions / validate

Trailing spaces not allowed
const signal = new UpdaterSignal(this.appUpdater)
signal.progress((info) => {
this._progress = info.transferred
this._total = info.total
this.update(info.delta)
})
await autoUpdater.downloadUpdate(this.cancellationToken)
await this.appUpdater.downloadUpdate(this.cancellationToken)
}

protected abort(): void {
Expand Down Expand Up @@ -241,38 +268,7 @@ export class ElectronUpdater implements LauncherAppUpdater {
}

async #getUpdateFromAutoUpdater(): Promise<ReleaseInfo> {
const gfw = await this.app.registry.get(kGFW)

if (gfw.inside) {
// @ts-ignore
AppUpdater.prototype.getUpdateInfoAndProvider = async function (this: AppUpdater) {
const result = await getUpdateInfoAndProvider.call(this)
const provider = result.provider

if (kPatched in provider) {
return result
}

const resolveFiles = provider.resolveFiles
Object.assign(provider, {
[kPatched]: true,
resolveFiles: function (this: Provider<UpdateInfo>, inf: UpdateInfo) {
const result = resolveFiles.call(provider, inf)
return result.map((i) => {
const pathname = i.url.pathname
return {
...i,
url: new URL(`${AZURE_CDN}/releases/${basename(pathname)}`),
}
})
},
})
return result
}
} else {
// @ts-ignore
AppUpdater.prototype.getUpdateInfoAndProvider = getUpdateInfoAndProvider
}
const autoUpdater = updater.autoUpdater

this.logger.log(`Check update via ${autoUpdater.getFeedURL()}`)
const info = await autoUpdater.checkForUpdates()
Expand Down Expand Up @@ -370,7 +366,7 @@ export class ElectronUpdater implements LauncherAppUpdater {

downloadUpdateTask(updateInfo: ReleaseInfo): Task<void> {
if (updateInfo.operation === ElectronUpdateOperation.AutoUpdater) {
return new DownloadFullUpdateTask()
return new DownloadFullUpdateTask(this.app, updater.autoUpdater)
} else if (updateInfo.operation === ElectronUpdateOperation.Asar) {
const updatePath = join(this.app.appDataPath, 'pending_update')
return new DownloadAsarUpdateTask(this.app, updatePath, updateInfo.name)
Expand All @@ -388,7 +384,7 @@ export class ElectronUpdater implements LauncherAppUpdater {
if (updateInfo.operation === ElectronUpdateOperation.Asar) {
await this.quitAndInstallAsar()
} else {
autoUpdater.quitAndInstall()
updater.autoUpdater.quitAndInstall()
}
}
}
3 changes: 3 additions & 0 deletions xmcl-runtime/app/LauncherApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,21 @@ export interface LauncherApp {
on(channel: 'root-migrated', listener: (newRoot: string) => void): this
on(channel: 'service-call-end', listener: (serviceName: string, serviceMethod: string, duration: number, success: boolean) => void): this
on(channel: 'service-state-init', listener: (stateKey: string) => void): this
on(channel: 'download-cdn', listener: (reason: string, file: string) => void): this

once(channel: 'app-booted', listener: (manifest: InstalledAppManifest) => void): this
once(channel: 'window-all-closed', listener: () => void): this
once(channel: 'root-migrated', listener: (newRoot: string) => void): this
once(channel: 'service-call-end', listener: (serviceName: string, serviceMethod: string, duration: number, success: boolean) => void): this
once(channel: 'service-state-init', listener: (stateKey: string) => void): this
once(channel: 'download-cdn', listener: (reason: string, file: string) => void): this

emit(channel: 'app-booted', manifest: InstalledAppManifest): this
emit(channel: 'service-call-end', serviceName: string, serviceMethod: string, duration: number, success: boolean): this
emit(channel: 'window-all-closed'): boolean
emit(channel: 'root-migrated', root: string): this
emit(channel: 'service-state-init', stateKey: string): this
emit(channel: 'download-cdn', reason: string, file: string): this
}

export interface LogEmitter extends EventEmitter {
Expand Down
10 changes: 10 additions & 0 deletions xmcl-runtime/telemetry/pluginTelemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,16 @@ export const pluginTelemetry: LauncherAppPlugin = async (app) => {
appInsight.dispose()
})

app.on('download-cdn', (reason, file) => {
client.trackEvent({
name: 'download-cdn',
properties: {
reason,
file,
},
})
})

app.on('service-call-end', (serviceName, serviceMethod, duration, success) => {
// Disable request to reduce cost
// const shouldTrack = () => {
Expand Down

0 comments on commit 6262c73

Please sign in to comment.