mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Desktop: Seamless-Updates: implemented flow for prereleases (#10892)
This commit is contained in:
parent
b617a84696
commit
2afc2ca369
@ -1,6 +1,6 @@
|
||||
import Logger, { LoggerWrapper } from '@joplin/utils/Logger';
|
||||
import { PluginMessage } from './services/plugins/PluginRunner';
|
||||
import AutoUpdaterService from './services/autoUpdater/AutoUpdaterService';
|
||||
import AutoUpdaterService, { defaultUpdateInterval, initialUpdateStartup } from './services/autoUpdater/AutoUpdaterService';
|
||||
import type ShimType from '@joplin/lib/shim';
|
||||
const shim: typeof ShimType = require('@joplin/lib/shim').default;
|
||||
import { isCallbackUrl } from '@joplin/lib/callbackUrlUtils';
|
||||
@ -45,6 +45,7 @@ export default class ElectronAppWrapper {
|
||||
private initialCallbackUrl_: string = null;
|
||||
private updaterService_: AutoUpdaterService = null;
|
||||
private customProtocolHandler_: CustomProtocolHandler = null;
|
||||
private updatePollInterval_: ReturnType<typeof setTimeout>|null = null;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
public constructor(electronApp: any, env: string, profilePath: string|null, isDebugMode: boolean, initialCallbackUrl: string) {
|
||||
@ -363,7 +364,7 @@ export default class ElectronAppWrapper {
|
||||
}
|
||||
|
||||
public quit() {
|
||||
this.stopLookingForUpdates();
|
||||
this.stopPeriodicUpdateCheck();
|
||||
this.electronApp_.quit();
|
||||
}
|
||||
|
||||
@ -468,18 +469,30 @@ export default class ElectronAppWrapper {
|
||||
this.customProtocolHandler_ ??= handleCustomProtocols(logger);
|
||||
}
|
||||
|
||||
public initializeAutoUpdaterService(logger: LoggerWrapper, initializedShim: typeof ShimType, devMode: boolean, includePreReleases: boolean) {
|
||||
// Electron's autoUpdater has to be init from the main process
|
||||
public async initializeAutoUpdaterService(logger: LoggerWrapper, devMode: boolean, includePreReleases: boolean) {
|
||||
if (shim.isWindows() || shim.isMac()) {
|
||||
this.updaterService_ = new AutoUpdaterService(this.win_, logger, initializedShim, devMode, includePreReleases);
|
||||
this.updaterService_.startPeriodicUpdateCheck();
|
||||
if (!this.updaterService_) {
|
||||
this.updaterService_ = new AutoUpdaterService(this.win_, logger, devMode, includePreReleases);
|
||||
this.startPeriodicUpdateCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public stopLookingForUpdates() {
|
||||
if (this.updaterService_ !== null) {
|
||||
this.updaterService_.stopPeriodicUpdateCheck();
|
||||
private startPeriodicUpdateCheck = (updateInterval: number = defaultUpdateInterval): void => {
|
||||
this.stopPeriodicUpdateCheck();
|
||||
this.updatePollInterval_ = setInterval(() => {
|
||||
void this.updaterService_.checkForUpdates();
|
||||
}, updateInterval);
|
||||
setTimeout(this.updaterService_.checkForUpdates, initialUpdateStartup);
|
||||
};
|
||||
|
||||
private stopPeriodicUpdateCheck = (): void => {
|
||||
if (this.updatePollInterval_) {
|
||||
clearInterval(this.updatePollInterval_);
|
||||
this.updatePollInterval_ = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public getCustomProtocolHandler() {
|
||||
return this.customProtocolHandler_;
|
||||
|
@ -404,6 +404,16 @@ class Application extends BaseApplication {
|
||||
eventManager.on(EventName.ResourceChange, handleResourceChange);
|
||||
}
|
||||
|
||||
private async setupAutoUpdaterService() {
|
||||
if (Setting.value('featureFlag.autoUpdaterServiceEnabled')) {
|
||||
await bridge().electronApp().initializeAutoUpdaterService(
|
||||
Logger.create('AutoUpdaterService'),
|
||||
Setting.value('env') === 'dev',
|
||||
Setting.value('autoUpdate.includePreReleases'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||
public async start(argv: string[], startOptions: StartOptions = null): Promise<any> {
|
||||
// If running inside a package, the command line, instead of being "node.exe <path> <flags>" is "joplin.exe <flags>" so
|
||||
@ -688,14 +698,7 @@ class Application extends BaseApplication {
|
||||
SearchEngine.instance().scheduleSyncTables();
|
||||
});
|
||||
|
||||
if (Setting.value('featureFlag.autoUpdaterServiceEnabled')) {
|
||||
bridge().electronApp().initializeAutoUpdaterService(
|
||||
Logger.create('AutoUpdaterService'),
|
||||
shim,
|
||||
Setting.value('env') === 'dev',
|
||||
Setting.value('autoUpdate.includePreReleases'),
|
||||
);
|
||||
}
|
||||
await this.setupAutoUpdaterService();
|
||||
|
||||
// setTimeout(() => {
|
||||
// void populateDatabase(reg.db(), {
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { BrowserWindow } from 'electron';
|
||||
import { autoUpdater, UpdateInfo } from 'electron-updater';
|
||||
import path = require('path');
|
||||
import { setInterval } from 'timers';
|
||||
import Logger, { LoggerWrapper } from '@joplin/utils/Logger';
|
||||
import type ShimType from '@joplin/lib/shim';
|
||||
const shim: typeof ShimType = require('@joplin/lib/shim').default;
|
||||
import { GitHubRelease, GitHubReleaseAsset } from '../../utils/checkForUpdatesUtils';
|
||||
import * as semver from 'semver';
|
||||
|
||||
export enum AutoUpdaterEvents {
|
||||
CheckingForUpdate = 'checking-for-update',
|
||||
@ -14,59 +16,36 @@ export enum AutoUpdaterEvents {
|
||||
UpdateDownloaded = 'update-downloaded',
|
||||
}
|
||||
|
||||
const defaultUpdateInterval = 12 * 60 * 60 * 1000;
|
||||
const initialUpdateStartup = 5 * 1000;
|
||||
export const defaultUpdateInterval = 12 * 60 * 60 * 1000;
|
||||
export const initialUpdateStartup = 5 * 1000;
|
||||
const releasesLink = 'https://objects.joplinusercontent.com/r/releases';
|
||||
|
||||
export interface AutoUpdaterServiceInterface {
|
||||
startPeriodicUpdateCheck(interval?: number): void;
|
||||
stopPeriodicUpdateCheck(): void;
|
||||
checkForUpdates(): void;
|
||||
updateApp(): void;
|
||||
}
|
||||
|
||||
export default class AutoUpdaterService implements AutoUpdaterServiceInterface {
|
||||
private window_: BrowserWindow;
|
||||
private logger_: LoggerWrapper;
|
||||
private initializedShim_: typeof ShimType;
|
||||
private devMode_: boolean;
|
||||
private updatePollInterval_: ReturnType<typeof setInterval>|null = null;
|
||||
private enableDevMode = true; // force the updater to work in "dev" mode
|
||||
private enableAutoDownload = false; // automatically download an update when it is found
|
||||
private autoInstallOnAppQuit = false; // automatically install the downloaded update once the user closes the application
|
||||
private includePreReleases_ = false;
|
||||
private allowDowngrade = false;
|
||||
|
||||
public constructor(mainWindow: BrowserWindow, logger: LoggerWrapper, initializedShim: typeof ShimType, devMode: boolean, includePreReleases: boolean) {
|
||||
public constructor(mainWindow: BrowserWindow, logger: LoggerWrapper, devMode: boolean, includePreReleases: boolean) {
|
||||
this.window_ = mainWindow;
|
||||
this.logger_ = logger;
|
||||
this.initializedShim_ = initializedShim;
|
||||
this.devMode_ = devMode;
|
||||
this.includePreReleases_ = includePreReleases;
|
||||
this.configureAutoUpdater();
|
||||
}
|
||||
|
||||
public startPeriodicUpdateCheck = (interval: number = defaultUpdateInterval): void => {
|
||||
this.stopPeriodicUpdateCheck();
|
||||
this.updatePollInterval_ = this.initializedShim_.setInterval(() => {
|
||||
void this.checkForUpdates();
|
||||
}, interval);
|
||||
this.initializedShim_.setTimeout(this.checkForUpdates, initialUpdateStartup);
|
||||
};
|
||||
|
||||
public stopPeriodicUpdateCheck = (): void => {
|
||||
if (this.updatePollInterval_) {
|
||||
this.initializedShim_.clearInterval(this.updatePollInterval_);
|
||||
this.updatePollInterval_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
public checkForUpdates = async (): Promise<void> => {
|
||||
try {
|
||||
if (this.includePreReleases_) {
|
||||
// If this is set to true, then it will compare the versions semantically and it will also look at tags, so we need to manually get the latest pre-release
|
||||
this.logger_.info('To be implemented...');
|
||||
} else {
|
||||
await autoUpdater.checkForUpdates();
|
||||
}
|
||||
await this.fetchLatestRelease();
|
||||
} catch (error) {
|
||||
this.logger_.error('Failed to check for updates:', error);
|
||||
if (error.message.includes('ERR_CONNECTION_REFUSED')) {
|
||||
@ -75,6 +54,63 @@ export default class AutoUpdaterService implements AutoUpdaterServiceInterface {
|
||||
}
|
||||
};
|
||||
|
||||
public updateApp = (): void => {
|
||||
autoUpdater.quitAndInstall(false, true);
|
||||
};
|
||||
|
||||
private fetchLatestReleases = async (): Promise<GitHubRelease[]> => {
|
||||
const response = await fetch(releasesLink);
|
||||
|
||||
if (!response.ok) {
|
||||
const responseText = await response.text();
|
||||
throw new Error(`Cannot get latest release info: ${responseText.substr(0, 500)}`);
|
||||
}
|
||||
|
||||
return (await response.json()) as GitHubRelease[];
|
||||
};
|
||||
|
||||
private fetchLatestRelease = async (): Promise<void> => {
|
||||
try {
|
||||
const releases = await this.fetchLatestReleases();
|
||||
|
||||
const sortedReleasesByVersion = releases.sort((a, b) => {
|
||||
return semver.rcompare(a.tag_name, b.tag_name);
|
||||
});
|
||||
const filteredReleases = sortedReleasesByVersion.filter(release => {
|
||||
return this.includePreReleases_ || !release.prerelease;
|
||||
});
|
||||
const release = filteredReleases[0];
|
||||
|
||||
if (release) {
|
||||
let assetUrl = null;
|
||||
|
||||
if (shim.isWindows()) {
|
||||
const asset = release.assets.find((asset: GitHubReleaseAsset) => asset.name === 'latest.yml');
|
||||
if (asset) {
|
||||
assetUrl = asset.browser_download_url.replace('/latest.yml', '');
|
||||
}
|
||||
} else if (shim.isMac()) {
|
||||
const asset = release.assets.find((asset: GitHubReleaseAsset) => asset.name === 'latest-mac.yml');
|
||||
if (asset) {
|
||||
assetUrl = asset.browser_download_url.replace('/latest-mac.yml', '');
|
||||
}
|
||||
}
|
||||
|
||||
if (assetUrl) {
|
||||
autoUpdater.setFeedURL({
|
||||
provider: 'generic',
|
||||
url: assetUrl,
|
||||
});
|
||||
await autoUpdater.checkForUpdates();
|
||||
} else {
|
||||
this.logger_.error('No suitable update asset found for this platform.');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger_.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
private configureAutoUpdater = (): void => {
|
||||
autoUpdater.logger = (this.logger_) as Logger;
|
||||
if (this.devMode_) {
|
||||
@ -124,8 +160,4 @@ export default class AutoUpdaterService implements AutoUpdaterServiceInterface {
|
||||
private promptUserToUpdate = async (info: UpdateInfo): Promise<void> => {
|
||||
this.window_.webContents.send(AutoUpdaterEvents.UpdateDownloaded, info);
|
||||
};
|
||||
|
||||
public updateApp = (): void => {
|
||||
autoUpdater.quitAndInstall(false, true);
|
||||
};
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ export interface CheckForUpdateOptions {
|
||||
includePreReleases?: boolean;
|
||||
}
|
||||
|
||||
interface GitHubReleaseAsset {
|
||||
export interface GitHubReleaseAsset {
|
||||
name: string;
|
||||
browser_download_url: string;
|
||||
}
|
||||
|
@ -129,3 +129,4 @@ entypo
|
||||
Zocial
|
||||
agplv
|
||||
Famegear
|
||||
rcompare
|
Loading…
Reference in New Issue
Block a user