2019-07-30 09:35:42 +02:00
const { dialog } = require ( 'electron' ) ;
2018-05-14 12:08:33 +02:00
const { shim } = require ( 'lib/shim' ) ;
2018-03-09 22:59:12 +02:00
const { Logger } = require ( 'lib/logger.js' ) ;
const { _ } = require ( 'lib/locale.js' ) ;
2018-03-14 19:23:19 +02:00
const fetch = require ( 'node-fetch' ) ;
2018-05-14 12:08:33 +02:00
const { fileExtension } = require ( 'lib/path-utils.js' ) ;
2018-03-14 19:23:19 +02:00
const packageInfo = require ( './packageInfo.js' ) ;
const compareVersions = require ( 'compare-versions' ) ;
2018-01-31 00:35:50 +02:00
let autoUpdateLogger _ = new Logger ( ) ;
let checkInBackground _ = false ;
2018-02-16 01:05:04 +02:00
let isCheckingForUpdate _ = false ;
let parentWindow _ = null ;
2018-01-31 00:35:50 +02:00
2018-02-16 01:05:04 +02:00
function showErrorMessageBox ( message ) {
return dialog . showMessageBox ( parentWindow _ , {
2018-03-09 22:59:12 +02:00
type : 'error' ,
2018-02-16 01:05:04 +02:00
message : message ,
} ) ;
}
function onCheckStarted ( ) {
2018-03-09 22:59:12 +02:00
autoUpdateLogger _ . info ( 'checkForUpdates: Starting...' ) ;
2018-02-16 01:05:04 +02:00
isCheckingForUpdate _ = true ;
}
function onCheckEnded ( ) {
2018-03-09 22:59:12 +02:00
autoUpdateLogger _ . info ( 'checkForUpdates: Done.' ) ;
2018-02-16 01:05:04 +02:00
isCheckingForUpdate _ = false ;
}
2019-01-12 01:40:05 +02:00
async function fetchLatestRelease ( options ) {
options = Object . assign ( { } , { includePreReleases : false } , options ) ;
2018-02-16 20:08:02 +02:00
2019-01-12 01:40:05 +02:00
let json = null ;
if ( options . includePreReleases ) {
// This end-point will include all releases, including pre-releases (but not draft), so we take
// whatever is the latest release. It might be the same as releases/latest, or it might be
// a pre-release.
const response = await fetch ( 'https://api.github.com/repos/laurent22/joplin/releases' ) ;
if ( ! response . ok ) {
const responseText = await response . text ( ) ;
2019-09-19 23:51:18 +02:00
throw new Error ( ` Cannot get latest release info: ${ responseText . substr ( 0 , 500 ) } ` ) ;
2019-01-12 01:40:05 +02:00
}
json = await response . json ( ) ;
2019-07-30 09:35:42 +02:00
if ( ! json . length ) throw new Error ( 'Cannot get latest release info (JSON)' ) ;
2019-01-12 01:40:05 +02:00
json = json [ 0 ] ;
} else {
const response = await fetch ( 'https://api.github.com/repos/laurent22/joplin/releases/latest' ) ;
2018-02-16 20:08:02 +02:00
2019-01-12 01:40:05 +02:00
if ( ! response . ok ) {
const responseText = await response . text ( ) ;
2019-09-19 23:51:18 +02:00
throw new Error ( ` Cannot get latest release info: ${ responseText . substr ( 0 , 500 ) } ` ) ;
2019-01-12 01:40:05 +02:00
}
json = await response . json ( ) ;
}
2019-07-30 09:35:42 +02:00
2018-03-14 19:23:19 +02:00
const version = json . tag _name . substr ( 1 ) ;
let downloadUrl = null ;
const platform = process . platform ;
for ( let i = 0 ; i < json . assets . length ; i ++ ) {
const asset = json . assets [ i ] ;
let found = false ;
2018-05-14 12:08:33 +02:00
const ext = fileExtension ( asset . name ) ;
if ( platform === 'win32' && ext === 'exe' ) {
if ( shim . isPortable ( ) ) {
found = asset . name == 'JoplinPortable.exe' ;
} else {
found = ! ! asset . name . match ( /^Joplin-Setup-[\d.]+\.exe$/ ) ;
}
} else if ( platform === 'darwin' && ext === 'dmg' ) {
2018-03-14 19:23:19 +02:00
found = true ;
2018-05-14 12:08:33 +02:00
} else if ( platform === 'linux' && ext === '.AppImage' ) {
2018-03-14 19:23:19 +02:00
found = true ;
}
if ( found ) {
downloadUrl = asset . browser _download _url ;
break ;
}
2018-02-06 15:11:59 +02:00
}
2018-03-14 19:23:19 +02:00
return {
version : version ,
downloadUrl : downloadUrl ,
notes : json . body ,
2018-05-14 13:18:00 +02:00
pageUrl : json . html _url ,
2019-01-12 01:40:05 +02:00
prerelease : json . prerelease ,
2018-03-14 19:23:19 +02:00
} ;
}
2018-01-31 00:35:50 +02:00
2019-10-12 00:44:58 +02:00
function truncateText ( text , length ) {
let truncated = text . substring ( 0 , length ) ;
const lastNewLine = truncated . lastIndexOf ( '\n' ) ;
// Cut off at a line break unless we'd be cutting off half the text
if ( lastNewLine > length / 2 ) {
truncated = ` ${ truncated . substring ( 0 , lastNewLine ) } \n ... ` ;
} else {
truncated = ` ${ truncated . trim ( ) } ... ` ;
}
return truncated ;
}
2019-01-12 01:40:05 +02:00
function checkForUpdates ( inBackground , window , logFilePath , options ) {
2018-02-16 01:05:04 +02:00
if ( isCheckingForUpdate _ ) {
2018-03-09 22:59:12 +02:00
autoUpdateLogger _ . info ( 'checkForUpdates: Skipping check because it is already running' ) ;
2018-02-16 01:05:04 +02:00
return ;
}
parentWindow _ = window ;
onCheckStarted ( ) ;
2018-01-31 00:35:50 +02:00
if ( logFilePath && ! autoUpdateLogger _ . targets ( ) . length ) {
autoUpdateLogger _ = new Logger ( ) ;
2018-03-09 22:59:12 +02:00
autoUpdateLogger _ . addTarget ( 'file' , { path : logFilePath } ) ;
2018-01-31 00:35:50 +02:00
autoUpdateLogger _ . setLevel ( Logger . LEVEL _DEBUG ) ;
2018-03-09 22:59:12 +02:00
autoUpdateLogger _ . info ( 'checkForUpdates: Initializing...' ) ;
2018-01-31 00:35:50 +02:00
}
checkInBackground _ = inBackground ;
2019-09-19 23:51:18 +02:00
autoUpdateLogger _ . info ( ` checkForUpdates: Checking with options ${ JSON . stringify ( options ) } ` ) ;
2019-01-12 01:40:05 +02:00
2020-01-23 22:33:01 +02:00
fetchLatestRelease ( options ) . then ( async ( release ) => {
2019-09-19 23:51:18 +02:00
autoUpdateLogger _ . info ( ` Current version: ${ packageInfo . version } ` ) ;
autoUpdateLogger _ . info ( ` Latest version: ${ release . version } ` ) ;
2019-01-12 01:40:05 +02:00
autoUpdateLogger _ . info ( 'Is Pre-release:' , release . prerelease ) ;
2018-05-14 13:18:00 +02:00
2018-03-14 19:23:19 +02:00
if ( compareVersions ( release . version , packageInfo . version ) <= 0 ) {
2020-03-14 01:57:34 +02:00
if ( ! checkInBackground _ ) {
await dialog . showMessageBox ( {
type : 'info' ,
message : _ ( 'Current version is up-to-date.' ) ,
buttons : [ _ ( 'OK' ) ] ,
} ) ;
}
2018-03-14 19:23:19 +02:00
} else {
2019-10-12 00:44:58 +02:00
const fullReleaseNotes = release . notes . trim ( ) ? ` \n \n ${ release . notes . trim ( ) } ` : '' ;
const MAX _RELEASE _NOTES _LENGTH = 1000 ;
const truncateReleaseNotes = fullReleaseNotes . length > MAX _RELEASE _NOTES _LENGTH ;
const releaseNotes = truncateReleaseNotes ? truncateText ( fullReleaseNotes , MAX _RELEASE _NOTES _LENGTH ) : fullReleaseNotes ;
2019-01-15 20:17:45 +02:00
const newVersionString = release . prerelease ? _ ( '%s (pre-release)' , release . version ) : release . version ;
2018-03-14 19:23:19 +02:00
2020-01-23 22:33:01 +02:00
const result = await dialog . showMessageBox ( parentWindow _ , {
2018-03-14 19:23:19 +02:00
type : 'info' ,
2019-09-19 23:51:18 +02:00
message : ` ${ _ ( 'An update is available, do you want to download it now?' ) } \n \n ${ _ ( 'Your version: %s' , packageInfo . version ) } \n ${ _ ( 'New version: %s' , newVersionString ) } ${ releaseNotes } ` ,
2019-10-12 00:44:58 +02:00
buttons : [ _ ( 'Yes' ) , _ ( 'No' ) ] . concat ( truncateReleaseNotes ? [ _ ( 'Full Release Notes' ) ] : [ ] ) ,
2018-03-14 19:23:19 +02:00
} ) ;
2020-01-23 22:33:01 +02:00
const buttonIndex = result . response ;
2018-05-14 13:18:00 +02:00
if ( buttonIndex === 0 ) require ( 'electron' ) . shell . openExternal ( release . downloadUrl ? release . downloadUrl : release . pageUrl ) ;
2019-10-12 00:44:58 +02:00
if ( buttonIndex === 2 ) require ( 'electron' ) . shell . openExternal ( release . pageUrl ) ;
2018-03-14 19:23:19 +02:00
}
2020-05-21 10:14:33 +02:00
} ) . catch ( error => {
2018-01-31 21:10:45 +02:00
autoUpdateLogger _ . error ( error ) ;
2018-02-16 01:05:04 +02:00
if ( ! checkInBackground _ ) showErrorMessageBox ( error . message ) ;
2018-03-14 19:23:19 +02:00
} ) . then ( ( ) => {
2018-02-16 01:05:04 +02:00
onCheckEnded ( ) ;
2018-03-14 19:23:19 +02:00
} ) ;
2018-01-31 00:35:50 +02:00
}
2019-07-30 09:35:42 +02:00
module . exports . checkForUpdates = checkForUpdates ;