2021-01-29 10:38:21 +01:00
|
|
|
import options from "commander"
|
|
|
|
|
|
|
|
import {execFileSync} from "child_process"
|
|
|
|
import {prepareFiles} from "./buildSrc/prepareMobileBuild.js"
|
|
|
|
import fs from "fs"
|
2021-06-28 13:03:54 +02:00
|
|
|
import path from "path"
|
2021-01-29 10:38:21 +01:00
|
|
|
|
2018-12-11 14:55:06 +01:00
|
|
|
|
2018-12-20 11:35:22 +01:00
|
|
|
/**
|
|
|
|
* Besides options below this script may require signing parameters passed as environment variables:
|
|
|
|
* 'APK_SIGN_ALIAS'
|
|
|
|
* 'APK_SIGN_STORE_PASS'
|
|
|
|
* 'APK_SIGN_KEY_PASS'
|
|
|
|
* 'APK_SIGN_STORE'
|
|
|
|
* 'ANDROID_HOME'
|
|
|
|
*/
|
|
|
|
|
2018-12-11 14:55:06 +01:00
|
|
|
options
|
2021-06-28 13:03:54 +02:00
|
|
|
.usage('[options] [test|prod|local] ')
|
|
|
|
.arguments('<target>')
|
2018-12-11 15:23:03 +01:00
|
|
|
.option('-b, --buildtype <type>', 'gradle build type', /^(debugDist|debug|release|releaseTest)$/i, 'release')
|
2019-02-27 11:16:51 +01:00
|
|
|
.option('-w --webclient <client>', 'choose web client build', /^(make|dist)$/i, 'dist')
|
2018-12-11 14:55:06 +01:00
|
|
|
.parse(process.argv)
|
|
|
|
|
2020-02-07 15:44:03 +01:00
|
|
|
const BUILD_TOOLS_V = "28.0.3"
|
2021-06-28 13:03:54 +02:00
|
|
|
const log = (...messages) => console.log("\nBUILD:", ...messages, "\n")
|
|
|
|
|
|
|
|
buildAndroid({
|
|
|
|
host: options.args[0] || 'prod',
|
|
|
|
webClient: options.webclient,
|
|
|
|
buildType: options.buildtype,
|
|
|
|
}).catch(e => {
|
|
|
|
console.error(e)
|
|
|
|
process.exit(1)
|
2019-02-01 11:42:51 +01:00
|
|
|
})
|
|
|
|
|
2021-06-28 13:03:54 +02:00
|
|
|
async function buildAndroid({host, buildType, webClient}) {
|
2019-07-08 11:47:35 +02:00
|
|
|
|
2021-06-28 13:03:54 +02:00
|
|
|
log(`Starting build with build type: ${buildType}, webclient: ${webClient}, host: ${host}`)
|
2018-12-11 14:55:06 +01:00
|
|
|
|
2021-06-28 13:03:54 +02:00
|
|
|
runCommand('node', [webClient, `${host}`], {
|
|
|
|
stdio: [null, process.stdout, process.stderr]
|
|
|
|
})
|
2018-12-11 14:55:06 +01:00
|
|
|
|
2021-06-28 13:03:54 +02:00
|
|
|
prepareFiles(webClient)
|
2018-12-11 14:55:06 +01:00
|
|
|
|
2021-06-28 13:03:54 +02:00
|
|
|
try {
|
|
|
|
log("cleaning 'build/app-android'")
|
|
|
|
await fs.promises.rm("build/app-android", {recursive: true})
|
|
|
|
} catch (e) {
|
|
|
|
// Ignoring the error if the folder is not there
|
2018-12-11 14:55:06 +01:00
|
|
|
}
|
|
|
|
|
2021-06-28 13:03:54 +02:00
|
|
|
log("Starting build: ", buildType)
|
|
|
|
|
|
|
|
runCommand('./gradlew', [`assemble${buildType}`], {
|
|
|
|
cwd: './app-android/',
|
2021-06-18 11:10:33 +02:00
|
|
|
})
|
2021-01-29 10:38:21 +01:00
|
|
|
|
2021-06-28 13:03:54 +02:00
|
|
|
|
|
|
|
let apkPath
|
|
|
|
|
|
|
|
switch (buildType) {
|
|
|
|
case 'debugDist':
|
|
|
|
apkPath = 'app/build/outputs/apk/debugDist/app-debugDist.apk'
|
|
|
|
break;
|
|
|
|
case 'debug':
|
|
|
|
apkPath = 'app/build/outputs/apk/debug/app-debug.apk'
|
|
|
|
break;
|
|
|
|
case 'releaseTest':
|
|
|
|
apkPath = 'app/build/outputs/apk/releaseTest/app-releaseTest-unsigned.apk'
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
apkPath = 'app/build/outputs/apk/release/app-release-unsigned.apk'
|
|
|
|
}
|
|
|
|
|
2021-01-29 10:38:21 +01:00
|
|
|
const {version} = JSON.parse(await fs.promises.readFile("package.json", "utf8"))
|
2021-06-28 13:03:54 +02:00
|
|
|
|
|
|
|
await fs.promises.mkdir("build/app-android", {recursive: true})
|
|
|
|
|
|
|
|
const outPath = `./build/app-android/tutanota-${version}-${buildType}.apk`
|
|
|
|
if (buildType === 'release' || buildType === 'releaseTest') {
|
|
|
|
await signAndroidApp({apkPath, outPath})
|
2021-01-29 10:38:21 +01:00
|
|
|
} else {
|
2021-06-28 13:03:54 +02:00
|
|
|
log("Skipping signing because build was not run as release or releaseTest")
|
|
|
|
await fs.promises.rename(path.join("app-android", apkPath), outPath)
|
2021-01-29 10:38:21 +01:00
|
|
|
}
|
2021-06-28 13:03:54 +02:00
|
|
|
|
|
|
|
log(`Build complete. The APK is located at: ${outPath}`)
|
2018-12-11 14:55:06 +01:00
|
|
|
}
|
2021-06-18 11:10:33 +02:00
|
|
|
|
2021-06-28 13:03:54 +02:00
|
|
|
async function signAndroidApp({apkPath, outPath}) {
|
|
|
|
|
|
|
|
const keyAlias = getEnv('APK_SIGN_ALIAS')
|
|
|
|
const storePass = getEnv('APK_SIGN_STORE_PASS')
|
|
|
|
const keyPass = getEnv('APK_SIGN_KEY_PASS')
|
|
|
|
const keyStore = getEnv('APK_SIGN_STORE')
|
|
|
|
const androidHome = getEnv('ANDROID_HOME')
|
|
|
|
|
|
|
|
// see https://developer.android.com/studio/publish/app-signing#signing-manually
|
|
|
|
// jarsigner must be run before zipalign
|
|
|
|
runCommand('/opt/jdk1.8.0_112/bin/jarsigner', [
|
|
|
|
'-verbose',
|
|
|
|
'-strict',
|
|
|
|
'-keystore', keyStore,
|
|
|
|
'-storepass', storePass,
|
|
|
|
'-keypass', keyPass,
|
|
|
|
'./app-android/' + apkPath,
|
|
|
|
keyAlias
|
|
|
|
])
|
|
|
|
|
|
|
|
// Android requires all resources to be aligned for mmap. Must be done.
|
|
|
|
runCommand(`${androidHome}/build-tools/${BUILD_TOOLS_V}/zipalign`, [
|
|
|
|
'4',
|
|
|
|
'app-android/' + apkPath,
|
|
|
|
outPath
|
|
|
|
])
|
|
|
|
}
|
|
|
|
|
|
|
|
function runCommand(command, args, options) {
|
2021-06-18 11:10:33 +02:00
|
|
|
try {
|
2021-06-28 13:03:54 +02:00
|
|
|
log("command:", `${command} ${args.join(" ")}`)
|
|
|
|
return execFileSync(command, args, options)
|
2021-06-18 11:10:33 +02:00
|
|
|
} catch (e) {
|
2021-06-28 13:03:54 +02:00
|
|
|
// original e contains lots of noise. `e.stack` has enough for debugging
|
|
|
|
throw new Error(e.stack)
|
2021-06-18 11:10:33 +02:00
|
|
|
}
|
|
|
|
}
|
2021-06-28 13:03:54 +02:00
|
|
|
|
|
|
|
function getEnv(name) {
|
|
|
|
if (!(name in process.env)) {
|
|
|
|
throw new Error(`${name} is not set`)
|
|
|
|
}
|
|
|
|
return process.env[name]
|
|
|
|
}
|