From 7ea4f570cb9d3c9d26b8df737f22f333bc805d69 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Fri, 6 Nov 2020 18:45:45 +0000 Subject: [PATCH] Tools: Refactor Android release script --- package.json | 1 + packages/app-mobile/.gitignore | 1 + packages/app-mobile/android/app/build.gradle | 4 +- packages/app-mobile/android/gradle.properties | 1 + packages/app-mobile/gulpfile.js | 3 ++ packages/app-mobile/package.json | 2 +- packages/app-mobile/tools/prepareRelease.js | 32 +++++++++++++ packages/tools/release-android.js | 31 ++++++------ packages/tools/tool-utils.js | 48 +++++++++++++++++++ 9 files changed, 104 insertions(+), 19 deletions(-) create mode 100644 packages/app-mobile/tools/prepareRelease.js diff --git a/package.json b/package.json index 328807e02..cb0647865 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "clean": "lerna clean -y && lerna run clean", "linkChecker": "linkchecker https://joplinapp.org", "releaseDesktop": "node packages/tools/release-electron.js", + "releaseAndroid": "node packages/tools/release-android.js", "publishAll": "git pull && lerna version --no-git-tag-version && gulp completePublishAll" }, "husky": { diff --git a/packages/app-mobile/.gitignore b/packages/app-mobile/.gitignore index b51f98e02..0c730cb52 100644 --- a/packages/app-mobile/.gitignore +++ b/packages/app-mobile/.gitignore @@ -61,3 +61,4 @@ buck-out/ # Custom lib/csstojs/ lib/rnInjectedJs/ +dist/ \ No newline at end of file diff --git a/packages/app-mobile/android/app/build.gradle b/packages/app-mobile/android/app/build.gradle index 7c6405572..d0306f7cc 100644 --- a/packages/app-mobile/android/app/build.gradle +++ b/packages/app-mobile/android/app/build.gradle @@ -138,8 +138,8 @@ android { applicationId "net.cozic.joplin" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 2097602 - versionName "1.4.0" + versionCode 2097608 + versionName "1.4.6" ndk { abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64" } diff --git a/packages/app-mobile/android/gradle.properties b/packages/app-mobile/android/gradle.properties index 3bdbd3d4e..f166ae229 100644 --- a/packages/app-mobile/android/gradle.properties +++ b/packages/app-mobile/android/gradle.properties @@ -11,6 +11,7 @@ # The setting is particularly useful for tweaking memory settings. # Default value: -Xmx10248m -XX:MaxPermSize=256m # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit diff --git a/packages/app-mobile/gulpfile.js b/packages/app-mobile/gulpfile.js index eb1419490..35631f3a7 100644 --- a/packages/app-mobile/gulpfile.js +++ b/packages/app-mobile/gulpfile.js @@ -12,6 +12,9 @@ const tasks = { podInstall: { fn: require('./tools/podInstall'), }, + prepareRelease: { + fn: require('./tools/prepareRelease'), + }, // clean: { // fn: require('./tools/clean'), // }, diff --git a/packages/app-mobile/package.json b/packages/app-mobile/package.json index ae0b01866..01d11dd6e 100644 --- a/packages/app-mobile/package.json +++ b/packages/app-mobile/package.json @@ -16,7 +16,6 @@ "dependencies": { "@joplinapp/lib": "^1.0.9", "@joplinapp/renderer": "^1.0.17", - "@joplinapp/tools": "^1.0.9", "@react-native-community/clipboard": "^1.5.0", "@react-native-community/datetimepicker": "^3.0.3", "@react-native-community/geolocation": "^2.0.2", @@ -60,6 +59,7 @@ "valid-url": "^1.0.9" }, "devDependencies": { + "@joplinapp/tools": "^1.0.9", "@babel/core": "^7.11.6", "@babel/runtime": "^7.11.2", "@types/node": "^14.14.6", diff --git a/packages/app-mobile/tools/prepareRelease.js b/packages/app-mobile/tools/prepareRelease.js new file mode 100644 index 000000000..8d564711a --- /dev/null +++ b/packages/app-mobile/tools/prepareRelease.js @@ -0,0 +1,32 @@ +// This is to replace the symlinks inside node_modules with the actual packages +// as I assumed it was needed to build the final release. However it seems +// Android `assembleRelease` handles symlinks properly so maybe this is not +// needed after all ¯\_(ツ)_/¯ + +const { copyDir } = require('@joplinapp/tools/gulp/utils'); +const { rootDir, deleteLink, toSystemSlashes } = require('@joplinapp/tools/tool-utils'); +const mobileDir = `${rootDir}/packages/app-mobile`; + +module.exports = async function() { + const dirsToCopy = [ + 'fork-htmlparser2', + 'fork-sax', + 'lib', + 'renderer', + ]; + + const destDir = `${mobileDir}/node_modules/@joplinapp`; + + for (const dir of dirsToCopy) { + const destPath = toSystemSlashes(`${destDir}/${dir}`); + const sourcePath = toSystemSlashes(`${rootDir}/packages/${dir}`); + + console.info(`Copying ${sourcePath} => ${destPath}`); + + // TODO: copy symlink so that it can be restored + await deleteLink(destPath); + await copyDir(sourcePath, destPath, { + excluded: ['node_modules'], + }); + } +}; diff --git a/packages/tools/release-android.js b/packages/tools/release-android.js index 00856935a..be716bdf2 100644 --- a/packages/tools/release-android.js +++ b/packages/tools/release-android.js @@ -1,13 +1,13 @@ const fs = require('fs-extra'); -const { execCommand, execCommandWithPipes, githubRelease, githubOauthToken, fileExists } = require('./tool-utils.js'); +const { execCommandVerbose, execCommandWithPipes, githubRelease, githubOauthToken, fileExists } = require('./tool-utils.js'); const path = require('path'); const fetch = require('node-fetch'); const uriTemplate = require('uri-template'); const projectName = 'joplin-android'; -const rnDir = `${__dirname}/../packages/app-mobile`; +const rnDir = `${__dirname}/../../packages/app-mobile`; const rootDir = path.dirname(__dirname); -const releaseDir = `${rootDir}/_releases`; +const releaseDir = `${rnDir}/dist`; // function wslToWinPath(wslPath) { // const s = wslPath.split('/'); @@ -84,7 +84,8 @@ async function createRelease(name, tagName, version) { console.info(`Building APK file v${suffix}...`); let restoreDir = null; - let apkBuildCmd = 'assembleRelease -PbuildDir=build'; + let apkBuildCmd = ''; + const apkBuildCmdArgs = ['assembleRelease', '-PbuildDir=build']; if (await fileExists('/mnt/c/Windows/System32/cmd.exe')) { // In recent versions (of Gradle? React Native?), running gradlew.bat from WSL throws the following error: @@ -114,14 +115,12 @@ async function createRelease(name, tagName, version) { apkBuildCmd = ''; } else { process.chdir(`${rnDir}/android`); - apkBuildCmd = `./gradlew ${apkBuildCmd}`; + apkBuildCmd = './gradlew'; restoreDir = rootDir; } if (apkBuildCmd) { - console.info(apkBuildCmd); - const output = await execCommand(apkBuildCmd); - console.info(output); + await execCommandVerbose(apkBuildCmd, apkBuildCmdArgs); } if (restoreDir) process.chdir(restoreDir); @@ -129,11 +128,11 @@ async function createRelease(name, tagName, version) { await fs.mkdirp(releaseDir); console.info(`Copying APK to ${apkFilePath}`); - await fs.copy('packages/app-mobile/android/app/build/outputs/apk/release/app-release.apk', apkFilePath); + await fs.copy('app-mobile/android/app/build/outputs/apk/release/app-release.apk', apkFilePath); if (name === 'main') { console.info(`Copying APK to ${releaseDir}/joplin-latest.apk`); - await fs.copy('packages/app-mobile/android/app/build/outputs/apk/release/app-release.apk', `${releaseDir}/joplin-latest.apk`); + await fs.copy('app-mobile/android/app/build/outputs/apk/release/app-release.apk', `${releaseDir}/joplin-latest.apk`); } for (const filename in originalContents) { @@ -177,12 +176,12 @@ async function main() { await fs.writeFile('README.md', readmeContent); } - console.info(await execCommand('git pull')); - console.info(await execCommand('git add -A')); - console.info(await execCommand(`git commit -m "Android release v${version}"`)); - console.info(await execCommand(`git tag ${tagName}`)); - console.info(await execCommand('git push')); - console.info(await execCommand('git push --tags')); + await execCommandVerbose('git', ['pull']); + await execCommandVerbose('git', ['add', '-A']); + await execCommandVerbose('git', ['commit', '-m', `Android release v${version}`]); + await execCommandVerbose('git', ['tag', tagName]); + await execCommandVerbose('git', ['push']); + await execCommandVerbose('git', ['push', '--tags']); console.info(`Creating GitHub release ${tagName}...`); diff --git a/packages/tools/tool-utils.js b/packages/tools/tool-utils.js index 7543ff0fd..0fe1e8b86 100644 --- a/packages/tools/tool-utils.js +++ b/packages/tools/tool-utils.js @@ -1,5 +1,7 @@ const fetch = require('node-fetch'); const fs = require('fs-extra'); +const execa = require('execa'); +const { execSync } = require('child_process'); const toolUtils = {}; @@ -21,6 +23,30 @@ toolUtils.execCommand = function(command) { }); }; +function quotePath(path) { + if (!path) return ''; + if (path.indexOf('"') < 0 && path.indexOf(' ') < 0) return path; + path = path.replace(/"/, '\\"'); + return `"${path}"`; +} + +function commandToString(commandName, args = []) { + const output = [quotePath(commandName)]; + + for (const arg of args) { + output.push(quotePath(arg)); + } + + return output.join(' '); +} + +toolUtils.execCommandVerbose = function(commandName, args = []) { + console.info(`> ${commandToString(commandName, args)}`); + const promise = execa(commandName, args); + promise.stdout.pipe(process.stdout); + return promise; +}; + toolUtils.execCommandWithPipes = function(executable, args) { const spawn = require('child_process').spawn; @@ -41,6 +67,28 @@ toolUtils.execCommandWithPipes = function(executable, args) { }); }; +toolUtils.toSystemSlashes = function(path) { + const os = process.platform; + if (os === 'win32') return path.replace(/\//g, '\\'); + return path.replace(/\\/g, '/'); +}; + +toolUtils.deleteLink = async function(path) { + if (toolUtils.isWindows()) { + try { + execSync(`rmdir "${toolUtils.toSystemSlashes(path)}"`, { stdio: 'pipe' }); + } catch (error) { + // console.info('Error: ' + error.message); + } + } else { + try { + fs.unlinkSync(toolUtils.toSystemSlashes(path)); + } catch (error) { + // ignore + } + } +}; + toolUtils.credentialDir = async function() { const username = require('os').userInfo().username;