Last active
January 24, 2024 03:36
-
-
Save daqi/39ad2082da525b0c08de79c7cc34fddb to your computer and use it in GitHub Desktop.
linux-updater
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { EventEmitter } from 'events'; | |
import { | |
eq as isVersionsEqual, | |
gt as isVersionGreaterThan, | |
lt as isVersionLessThan, | |
parse as parseVersion, | |
} from 'semver'; | |
import { safeLoad } from 'js-yaml'; | |
import { app, shell, net } from 'electron'; | |
import { Logger } from '../logger'; | |
function newError(message: string, code: string) { | |
const error = new Error(message); | |
(error as NodeJS.ErrnoException).code = code; | |
return error; | |
} | |
type UpdateCheckResult = any; | |
type UpdateInfo = { | |
version: string; | |
files: { url: string }[]; | |
}; | |
type Option = { | |
url: string; | |
downloadUrl: string; | |
}; | |
class LinuxUpdater extends EventEmitter { | |
_logger = Logger; | |
checkForUpdatesPromise: Promise<UpdateCheckResult> | null = null; | |
currentVersion: string; | |
allowDowngrade = false; | |
url: string; | |
downloadUrl: string; | |
constructor(options: Option) { | |
super(); | |
this.url = options.url; | |
this.downloadUrl = options.downloadUrl; | |
this.currentVersion = app.getVersion(); | |
} | |
checkForUpdates(): Promise<UpdateCheckResult> { | |
let checkForUpdatesPromise = this.checkForUpdatesPromise; | |
if (checkForUpdatesPromise != null) { | |
this._logger.info('[LinuxUpdater] Checking for update (already in progress)'); | |
return checkForUpdatesPromise; | |
} | |
const nullizePromise = () => (this.checkForUpdatesPromise = null); | |
this._logger.info('[LinuxUpdater] Checking for update'); | |
checkForUpdatesPromise = this.doCheckForUpdates() | |
.then((it) => { | |
nullizePromise(); | |
return it; | |
}) | |
.catch((e) => { | |
nullizePromise(); | |
this.emit('error', e, `Cannot check for updates: ${(e.stack || e).toString()}`); | |
throw e; | |
}); | |
this.checkForUpdatesPromise = checkForUpdatesPromise; | |
return checkForUpdatesPromise; | |
} | |
private async doCheckForUpdates(): Promise<UpdateCheckResult> { | |
this.emit('checking-for-update'); | |
const updateInfo = await this.getUpdateInfo(); | |
if (!(await this.isUpdateAvailable(updateInfo))) { | |
this._logger.info( | |
`[LinuxUpdater] Update for version ${this.currentVersion} is not available (latest version: ${ | |
updateInfo.version | |
}, downgrade is ${this.allowDowngrade ? 'allowed' : 'disallowed'}).` | |
); | |
this.emit('update-not-available', updateInfo); | |
return { | |
versionInfo: updateInfo, | |
updateInfo, | |
}; | |
} | |
this.onUpdateAvailable(updateInfo); | |
//noinspection ES6MissingAwait | |
return { | |
versionInfo: updateInfo, | |
updateInfo, | |
}; | |
} | |
protected onUpdateAvailable(updateInfo: UpdateInfo): void { | |
this._logger.info( | |
`[LinuxUpdater] Found version ${updateInfo.version} (url: ${updateInfo.files | |
.map((it) => it.url) | |
.join(', ')})` | |
); | |
this.emit('update-available', updateInfo); | |
} | |
private async getUpdateInfo(): Promise<UpdateInfo> { | |
const text = await new Promise<string>((rz, rj) => { | |
const request = net.request(this.url + `?noCache=${Date.now()}`); | |
let body = ''; | |
request.on('response', (response) => { | |
if (response.statusCode !== 200) { | |
rj(response.statusMessage); | |
return; | |
} | |
response.on('data', (chunk) => { | |
body += chunk.toString(); | |
}); | |
response.on('end', () => { | |
rz(body); | |
}); | |
}); | |
request.on('error', (err) => { | |
rj(err); | |
}); | |
request.end(); | |
}); | |
const info = safeLoad(text) as UpdateInfo; | |
return info; | |
} | |
private async isUpdateAvailable(updateInfo: UpdateInfo): Promise<boolean> { | |
const latestVersion = parseVersion(updateInfo.version); | |
if (latestVersion == null) { | |
throw newError( | |
`This file could not be downloaded, or the latest version (from update server) does not have a valid semver version: "${updateInfo.version}"`, | |
'ERR_UPDATER_INVALID_VERSION' | |
); | |
} | |
const currentVersion = this.currentVersion; | |
if (isVersionsEqual(latestVersion, currentVersion)) { | |
return false; | |
} | |
const isLatestVersionNewer = isVersionGreaterThan(latestVersion, currentVersion); | |
const isLatestVersionOlder = isVersionLessThan(latestVersion, currentVersion); | |
if (isLatestVersionNewer) { | |
return true; | |
} | |
return this.allowDowngrade && isLatestVersionOlder; | |
} | |
public goDownload() { | |
shell.openExternal(this.downloadUrl); | |
} | |
} | |
const url = 'https://artifact.xxxxxxxx.com/download/ynote-electron/latest-linux.yml'; | |
const downloadUrl = 'https://note.youdao.com/download.html'; | |
let _autoUpdater: any; | |
export declare const linuxUpdater: LinuxUpdater; | |
Object.defineProperty(exports, 'linuxUpdater', { | |
enumerable: true, | |
get: () => { | |
if (_autoUpdater) return _autoUpdater; | |
_autoUpdater = new LinuxUpdater({ url, downloadUrl }); | |
return _autoUpdater; | |
}, | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment