2023-03-19 15:37:07 +00:00
import { rootDir } from './tool-utils' ;
2021-07-08 15:04:03 +02:00
import * as moment from 'moment' ;
2023-03-19 15:37:07 +00:00
import { execCommand } from '@joplin/utils' ;
2021-06-11 18:24:59 +02:00
2022-02-23 09:02:13 +00:00
interface Argv {
dryRun? : boolean ;
pushImages? : boolean ;
repository? : string ;
tagName? : string ;
2025-03-30 12:01:39 +02:00
platform? : string ;
source? : string ;
addLatestTag? : boolean ;
2025-07-31 21:35:03 +01:00
dockerFile? : string ;
2025-03-30 12:01:39 +02:00
}
function parseArgv ( ) : Argv {
return require ( 'yargs' )
. scriptName ( 'yarn buildServerDocker' )
. usage ( '$0 --repository OWNER/IMAGE [args]' )
2025-07-31 21:35:03 +01:00
. option ( 'dockerFile' , {
describe : 'Dockerfile - either Dockerfile.server or Dockerfile.transcribe' ,
demandOption : true ,
type : 'string' ,
} )
. option ( 'repository' , {
2025-03-30 12:01:39 +02:00
describe : 'Target image repository. Usually in format `OWNER/NAME`' ,
demandOption : true ,
type : 'string' ,
} )
2025-07-31 21:35:03 +01:00
. option ( 'tagName' , {
2025-03-30 12:01:39 +02:00
describe : 'Base image tag. Usually should be in format `server-v1.2.3` or `server-v1.2.3-beta`. The latest `server-v*` git tag will be used by default.' ,
type : 'string' ,
} )
2025-07-31 21:35:03 +01:00
. option ( 'addLatestTag' , {
2025-03-30 12:01:39 +02:00
describe : 'Add `latest` tag even for pre-release images.' ,
type : 'boolean' ,
default : false ,
} )
. option ( 'platform' , {
describe : 'Comma separated list of target image platforms. E.g. `linux/amd64` or `linux/amd64,linux/arm64`' ,
type : 'string' ,
default : 'linux/amd64' ,
} )
. option ( 'source' , {
describe : 'Source Git repository for the images.' ,
type : 'string' ,
default : 'https://github.com/laurent22/joplin.git' ,
} )
2025-07-31 21:35:03 +01:00
. option ( 'pushImages' , {
2025-03-30 12:01:39 +02:00
describe : 'Publish images to target repository.' ,
type : 'boolean' ,
default : false ,
} )
. option ( 'dryRun' , {
describe : 'Do not call docker, just show command instead.' ,
type : 'boolean' ,
default : false ,
} )
. help ( )
. argv as Argv ;
2022-02-23 09:02:13 +00:00
}
2021-09-23 16:48:26 +01:00
export function getVersionFromTag ( tagName : string , isPreRelease : boolean ) : string {
2021-06-11 18:24:59 +02:00
const s = tagName . split ( '-' ) ;
2025-03-30 12:01:39 +02:00
const mainVersion = s [ 1 ] . replace ( /^(v)/ , '' ) ;
const metaComponents = s . slice ( 2 ) . filter ( item = > item !== 'beta' ) ;
// Append `git describe` components for pre release images. Mostly for case without `tagName` arg
const suffix = isPreRelease ? ` -beta ${ metaComponents . length > 0 ? ` . ${ metaComponents . join ( '.' ) } ` : '' } ` : '' ;
return mainVersion + suffix ;
2021-06-11 18:24:59 +02:00
}
2021-09-29 17:55:54 +01:00
export function getIsPreRelease ( _tagName : string ) : boolean {
// For now we only create pre-releases from CI. It's after, once the release
// has been proven stable, that it is tagged as "latest".
2025-03-30 19:38:18 +02:00
return false ;
2021-09-29 17:55:54 +01:00
// return tagName.indexOf('-beta') > 0;
2021-06-11 18:24:59 +02:00
}
async function main() {
2025-03-30 12:01:39 +02:00
const argv = parseArgv ( ) ;
if ( ! argv . tagName ) console . info ( 'No `--tag-name` was specified. A latest git tag will be used instead.' ) ;
2021-06-11 18:24:59 +02:00
2025-07-31 21:35:03 +01:00
console . info ( 'Raw arguments:' , argv ) ;
2025-03-30 12:01:39 +02:00
const dryRun = argv . dryRun ;
const addLatestTag = argv . addLatestTag ;
2025-07-31 21:35:03 +01:00
const dockerFile = argv . dockerFile ;
2025-03-30 12:01:39 +02:00
const pushImages = argv . pushImages ;
2022-02-23 09:02:13 +00:00
const repository = argv . repository ;
2025-03-30 12:01:39 +02:00
const tagName = argv . tagName || ` server- ${ await execCommand ( 'git describe --tags --match v*' , { showStdout : false } )} ` ;
const platform = argv . platform ;
const source = argv . source ;
2025-03-30 18:50:57 +02:00
const architecture = argv . platform . split ( '/' ) [ 1 ] ;
2025-03-30 12:01:39 +02:00
2021-06-11 18:24:59 +02:00
const isPreRelease = getIsPreRelease ( tagName ) ;
2021-06-19 15:14:19 +01:00
const imageVersion = getVersionFromTag ( tagName , isPreRelease ) ;
2021-07-08 15:04:03 +02:00
const buildDate = moment ( new Date ( ) . getTime ( ) ) . format ( 'YYYY-MM-DDTHH:mm:ssZ' ) ;
let revision = '' ;
try {
2023-03-19 15:37:07 +00:00
revision = await execCommand ( 'git rev-parse --short HEAD' , { showStdout : false } ) ;
2021-07-08 15:04:03 +02:00
} catch ( error ) {
console . info ( 'Could not get git commit: metadata revision field will be empty' ) ;
}
2025-03-30 12:01:39 +02:00
const buildArgs = [ ] ;
buildArgs . push ( ` BUILD_DATE=" ${ buildDate } " ` ) ;
buildArgs . push ( ` REVISION=" ${ revision } " ` ) ;
buildArgs . push ( ` VERSION=" ${ imageVersion } " ` ) ;
buildArgs . push ( ` SOURCE=" ${ source } " ` ) ;
2021-07-25 11:38:36 +02:00
const dockerTags : string [ ] = [ ] ;
2025-03-30 12:01:39 +02:00
const versionParts = imageVersion . split ( '.' ) ;
const patchVersionPart = versionParts [ 2 ] . split ( '-' ) [ 0 ] ;
dockerTags . push ( isPreRelease ? 'latest-beta' : 'latest' ) ;
dockerTags . push ( versionParts [ 0 ] + ( isPreRelease ? '-beta' : '' ) ) ;
dockerTags . push ( ` ${ versionParts [ 0 ] } . ${ versionParts [ 1 ] } ${ isPreRelease ? '-beta' : '' } ` ) ;
dockerTags . push ( ` ${ versionParts [ 0 ] } . ${ versionParts [ 1 ] } . ${ patchVersionPart } ${ isPreRelease ? '-beta' : '' } ` ) ;
if ( dockerTags . indexOf ( imageVersion ) < 0 ) {
dockerTags . push ( imageVersion ) ;
}
if ( addLatestTag && dockerTags . indexOf ( 'latest' ) < 0 ) {
dockerTags . push ( 'latest' ) ;
}
2021-06-11 18:24:59 +02:00
process . chdir ( rootDir ) ;
console . info ( ` Running from: ${ process . cwd ( ) } ` ) ;
2025-07-31 21:35:03 +01:00
console . info ( 'dockerFile:' , dockerFile ) ;
2025-03-30 12:01:39 +02:00
console . info ( 'repository:' , repository ) ;
2021-06-11 18:24:59 +02:00
console . info ( 'tagName:' , tagName ) ;
2025-03-30 12:01:39 +02:00
console . info ( 'platform:' , platform ) ;
2021-08-14 16:58:08 +01:00
console . info ( 'pushImages:' , pushImages ) ;
2021-06-11 18:24:59 +02:00
console . info ( 'imageVersion:' , imageVersion ) ;
console . info ( 'isPreRelease:' , isPreRelease ) ;
2021-07-25 11:38:36 +02:00
console . info ( 'Docker tags:' , dockerTags . join ( ', ' ) ) ;
2021-06-11 18:24:59 +02:00
2025-03-30 12:01:39 +02:00
const cliArgs = [ '--progress=plain' ] ;
cliArgs . push ( ` --platform ${ platform } ` ) ;
2025-03-30 18:50:57 +02:00
cliArgs . push ( . . . dockerTags . map ( tag = > ` --tag " ${ repository } : ${ architecture } - ${ tag } " ` ) ) ;
2025-03-30 12:01:39 +02:00
cliArgs . push ( . . . buildArgs . map ( arg = > ` --build-arg ${ arg } ` ) ) ;
if ( pushImages ) {
cliArgs . push ( '--push' ) ;
}
2025-07-31 21:35:03 +01:00
cliArgs . push ( ` --file ${ dockerFile } ` ) ;
2025-03-30 12:01:39 +02:00
cliArgs . push ( '.' ) ;
const dockerCommand = ` docker buildx build ${ cliArgs . join ( ' ' ) } ` ;
console . info ( 'exec:' , dockerCommand ) ;
2022-01-13 17:39:58 +00:00
if ( dryRun ) {
return ;
}
2023-03-19 15:37:07 +00:00
await execCommand ( dockerCommand ) ;
2021-06-11 18:24:59 +02:00
}
2021-09-23 16:48:26 +01:00
if ( require . main === module ) {
2023-03-09 16:25:21 +00:00
// eslint-disable-next-line promise/prefer-await-to-then
2021-09-23 16:48:26 +01:00
main ( ) . catch ( ( error ) = > {
console . error ( 'Fatal error' ) ;
console . error ( error ) ;
process . exit ( 1 ) ;
} ) ;
}