2023-09-13 20:01:27 +02:00
import { pathExists } from 'fs-extra' ;
import { readFile , writeFile } from 'fs/promises' ;
2024-02-20 00:50:10 +02:00
import { GitHubRelease , gitHubLatestReleases , gitHubLinkify } from './tool-utils' ;
2023-09-13 20:01:27 +02:00
import { config , createPost , createTopic , getForumTopPostByExternalId , getTopicByExternalId , updatePost } from './utils/discourse' ;
2023-09-27 01:01:52 +02:00
import { compareVersions } from 'compare-versions' ;
2023-09-13 20:01:27 +02:00
import dayjs = require ( 'dayjs' ) ;
2024-02-20 00:50:10 +02:00
import { getRootDir } from '@joplin/utils' ;
2023-09-13 20:01:27 +02:00
interface State {
2024-04-05 13:16:49 +02:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
2023-09-13 20:01:27 +02:00
processedReleases : Record < string , any > ;
}
2024-02-20 00:50:10 +02:00
enum Platform {
Desktop = 'Desktop' ,
Android = 'Android' ,
iOS = 'iOS' ,
}
2023-09-13 20:01:27 +02:00
const stateFilePath = ` ${ __dirname } /postPreReleasesToForum.json ` ;
const betaCategoryId = 10 ;
const loadState = async ( ) : Promise < State > = > {
if ( await pathExists ( stateFilePath ) ) {
const content = await readFile ( stateFilePath , 'utf-8' ) ;
return JSON . parse ( content ) as State ;
}
return {
processedReleases : { } ,
} ;
} ;
const saveState = async ( state : State ) = > {
2024-02-20 00:50:10 +02:00
await writeFile ( stateFilePath , JSON . stringify ( state , null , '\t' ) , 'utf-8' ) ;
2023-09-13 20:01:27 +02:00
} ;
2024-02-20 00:50:10 +02:00
const getPatchVersion = ( tagName : string ) = > {
if ( tagName . includes ( '-' ) ) tagName = tagName . split ( '-' ) [ 1 ] ;
return tagName . substring ( 1 ) ;
} ;
const getMinorVersion = ( tagName : string ) = > {
const s = getPatchVersion ( tagName ) . split ( '.' ) ;
2023-09-13 20:01:27 +02:00
return ` ${ s [ 0 ] } . ${ s [ 1 ] } ` ;
} ;
2024-02-20 00:50:10 +02:00
const getExternalId = ( platform : string , minorVersion : string ) = > {
let prefix = '' ;
if ( platform !== Platform . Desktop ) {
prefix = ` ${ platform . toLowerCase ( ) } - ` ;
}
return ` prerelease- ${ prefix } ${ minorVersion . replace ( /\./g , '-' ) } ` ;
} ;
const getDownloadInfo = ( platform : Platform ) = > {
const infos : Record < Platform , string > = {
[ Platform . Desktop ] : 'Download the latest pre-releases from here: <https://github.com/laurent22/joplin/releases>' ,
[ Platform . Android ] : 'Download the latest pre-releases from here: <https://github.com/laurent22/joplin-android/tags>' ,
[ Platform . iOS ] : 'In order to try the iOS pre-release, you will need to join the Joplin iOS beta program from here: <https://testflight.apple.com/join/p5iLVzrG>' ,
} ;
2023-09-13 20:01:27 +02:00
2024-02-20 00:50:10 +02:00
return infos [ platform ] ;
} ;
2023-09-13 20:01:27 +02:00
2024-02-20 00:50:10 +02:00
const getReleasesFromMarkdown = async ( filePath : string ) = > {
const content = await readFile ( filePath , 'utf-8' ) ;
const lines = content . split ( '\n' ) ;
const releases : GitHubRelease [ ] = [ ] ;
let currentRelease : GitHubRelease = null ;
for ( let line of lines ) {
line = line . trim ( ) ;
if ( ! line ) continue ;
if ( line . startsWith ( '##' ) ) {
const matches = line . match ( /## \[(.*?)\]/ ) ;
if ( ! matches ) throw new Error ( ` Could not parse version: ${ line } ` ) ;
const tag = matches [ 1 ] ;
currentRelease = {
tag_name : tag ,
body : '' ,
assets : [ ] ,
draft : false ,
html_url : ` https://github.com/laurent22/joplin-android/releases/tag/ ${ tag } ` ,
prerelease : false ,
upload_url : '' ,
} ;
releases . push ( currentRelease ) ;
continue ;
}
2023-09-13 20:01:27 +02:00
2024-02-20 00:50:10 +02:00
if ( currentRelease ) {
if ( currentRelease . body ) currentRelease . body += '\n' ;
currentRelease . body += line ;
}
}
2023-09-13 20:01:27 +02:00
2024-02-29 21:45:48 +02:00
releases . sort ( ( a , b ) = > compareVersions ( getPatchVersion ( a . tag_name ) , getPatchVersion ( b . tag_name ) ) ) ;
2024-02-20 00:50:10 +02:00
return releases ;
} ;
const processReleases = async ( releases : GitHubRelease [ ] , platform : Platform , startFromVersion : string , state : State ) = > {
2023-09-13 20:01:27 +02:00
for ( const release of releases ) {
const minorVersion = getMinorVersion ( release . tag_name ) ;
2024-02-20 00:50:10 +02:00
const patchVersion = getPatchVersion ( release . tag_name ) ;
2023-09-13 20:01:27 +02:00
if ( compareVersions ( startFromVersion , minorVersion ) > 0 ) continue ;
if ( ! state . processedReleases [ release . tag_name ] ) {
console . info ( ` Processing release ${ release . tag_name } ` ) ;
2024-02-20 00:50:10 +02:00
const externalId = getExternalId ( platform , minorVersion ) ;
2023-09-13 20:01:27 +02:00
2024-02-20 00:50:10 +02:00
const postBody = ` ## [v ${ patchVersion } ]( ${ release . html_url } ) \ n \ n ${ gitHubLinkify ( release . body ) } ` ;
2023-09-13 20:01:27 +02:00
let topic = await getTopicByExternalId ( externalId ) ;
2024-02-20 00:50:10 +02:00
const topicTitle = ` ${ platform } pre-release v ${ minorVersion } is now available (Updated ${ dayjs ( new Date ( ) ) . format ( 'DD/MM/YYYY' ) } ) ` ;
2023-09-13 20:01:27 +02:00
if ( ! topic ) {
console . info ( 'No topic exists - creating one...' ) ;
topic = await createTopic ( {
title : topicTitle ,
2024-02-20 00:50:10 +02:00
raw : ` ${ getDownloadInfo ( platform ) } \ n \ n* * * \ n \ n ${ postBody } ` ,
2023-09-13 20:01:27 +02:00
category : betaCategoryId ,
external_id : externalId ,
} ) ;
} else {
console . info ( 'A topic exists - appending the new pre-release to it...' ) ;
const topPost = await getForumTopPostByExternalId ( externalId ) ;
await updatePost ( topPost . id , {
title : topicTitle ,
raw : ` ${ topPost . raw } \ n \ n ${ postBody } ` ,
edit_reason : 'Auto-updated by script' ,
} ) ;
await createPost ( topic . id , {
raw : postBody ,
} ) ;
}
state . processedReleases [ release . tag_name ] = true ;
await saveState ( state ) ;
}
}
2024-02-20 00:50:10 +02:00
return state ;
} ;
const main = async ( ) = > {
const rootDir = await getRootDir ( ) ;
const argv = require ( 'yargs' ) . argv ;
config . key = argv . _ [ 0 ] ;
config . username = argv . _ [ 1 ] ;
let state = await loadState ( ) ;
{
const releases = await gitHubLatestReleases ( 1 , 50 ) ;
2024-02-29 21:45:48 +02:00
releases . sort ( ( a , b ) = > compareVersions ( a . tag_name , b . tag_name ) ) ;
2024-02-20 00:50:10 +02:00
state = await processReleases ( releases , Platform . Desktop , '2.13' , state ) ;
}
{
const releases = await getReleasesFromMarkdown ( ` ${ rootDir } /readme/about/changelog/android.md ` ) ;
state = await processReleases ( releases , Platform . Android , '2.14' , state ) ;
}
{
const releases = await getReleasesFromMarkdown ( ` ${ rootDir } /readme/about/changelog/ios.md ` ) ;
await processReleases ( releases , Platform . iOS , '12.14' , state ) ;
}
2023-09-13 20:01:27 +02:00
} ;
main ( ) . catch ( ( error ) = > {
console . error ( 'Fatal error' , error ) ;
process . exit ( 1 ) ;
} ) ;