1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-18 09:35:20 +02:00
joplin/packages/tools/release-android.ts

218 lines
7.8 KiB
TypeScript
Raw Normal View History

2021-05-16 19:38:36 +02:00
import * as fs from 'fs-extra';
2021-05-16 19:40:08 +02:00
import { execCommandVerbose, execCommandWithPipes, githubRelease, githubOauthToken, fileExists, gitPullTry, completeReleaseWithChangelog } from './tool-utils';
const path = require('path');
const fetch = require('node-fetch');
const uriTemplate = require('uri-template');
const projectName = 'joplin-android';
2020-11-26 21:42:05 +02:00
const rootDir = path.dirname(path.dirname(__dirname));
const rnDir = `${rootDir}/packages/app-mobile`;
2020-11-06 20:45:45 +02:00
const releaseDir = `${rnDir}/dist`;
2021-05-16 19:38:36 +02:00
interface Release {
downloadUrl: string;
apkFilename: string;
apkFilePath: string;
}
function increaseGradleVersionCode(content: string) {
const newContent = content.replace(/versionCode\s+(\d+)/, function(_a, versionCode: string) {
const n = Number(versionCode);
2019-09-19 23:51:18 +02:00
if (isNaN(n) || !n) throw new Error(`Invalid version code: ${versionCode}`);
return `versionCode ${n + 1}`;
});
if (newContent === content) throw new Error('Could not update version code');
return newContent;
}
2021-05-16 19:38:36 +02:00
function increaseGradleVersionName(content: string) {
const newContent = content.replace(/(versionName\s+"\d+?\.\d+?\.)(\d+)"/, function(_match, prefix: string, buildNum: string) {
const n = Number(buildNum);
2020-09-12 01:10:18 +02:00
if (isNaN(n)) throw new Error(`Invalid version code: ${buildNum}`);
2019-09-19 23:51:18 +02:00
return `${prefix + (n + 1)}"`;
});
if (newContent === content) throw new Error('Could not update version name');
return newContent;
}
function updateGradleConfig() {
2019-09-19 23:51:18 +02:00
let content = fs.readFileSync(`${rnDir}/android/app/build.gradle`, 'utf8');
content = increaseGradleVersionCode(content);
content = increaseGradleVersionName(content);
2019-09-19 23:51:18 +02:00
fs.writeFileSync(`${rnDir}/android/app/build.gradle`, content);
return content;
}
2021-05-16 19:38:36 +02:00
function gradleVersionName(content: string) {
const matches = content.match(/versionName\s+"(\d+?\.\d+?\.\d+)"/);
if (!matches || matches.length < 1) throw new Error('Cannot get gradle version name');
return matches[1];
}
2021-05-16 19:38:36 +02:00
async function createRelease(name: string, tagName: string, version: string): Promise<Release> {
const originalContents: Record<string, string> = {};
const suffix = version + (name === 'main' ? '' : `-${name}`);
2019-09-19 23:51:18 +02:00
console.info(`Creating release: ${suffix}`);
if (name === '32bit') {
const filename = `${rnDir}/android/app/build.gradle`;
let content = await fs.readFile(filename, 'utf8');
originalContents[filename] = content;
content = content.replace(/abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"/, 'abiFilters "armeabi-v7a", "x86"');
content = content.replace(/include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"/, 'include "armeabi-v7a", "x86"');
await fs.writeFile(filename, content);
}
2019-09-19 23:51:18 +02:00
const apkFilename = `joplin-v${suffix}.apk`;
const apkFilePath = `${releaseDir}/${apkFilename}`;
const downloadUrl = `https://github.com/laurent22/${projectName}/releases/download/${tagName}/${apkFilename}`;
process.chdir(rootDir);
2019-09-19 23:51:18 +02:00
console.info(`Running from: ${process.cwd()}`);
2019-09-19 23:51:18 +02:00
console.info(`Building APK file v${suffix}...`);
let restoreDir = null;
2020-11-06 20:45:45 +02:00
let apkBuildCmd = '';
const apkBuildCmdArgs = ['assembleRelease', '-PbuildDir=build'];
2019-06-15 10:44:34 +02:00
if (await fileExists('/mnt/c/Windows/System32/cmd.exe')) {
2019-06-15 19:58:09 +02:00
// In recent versions (of Gradle? React Native?), running gradlew.bat from WSL throws the following error:
2020-11-05 18:58:23 +02:00
// Error: Command failed: /mnt/c/Windows/System32/cmd.exe /c "cd packages\app-mobile\android && gradlew.bat assembleRelease -PbuildDir=build"
2019-06-15 19:58:09 +02:00
// FAILURE: Build failed with an exception.
// * What went wrong:
// Could not determine if Stdout is a console: could not get handle file information (errno 1)
// So we need to manually run the command from DOS, and then coming back here to finish the process once it's done.
// console.info('Run this command from DOS:');
// console.info('');
2020-11-05 18:58:23 +02:00
// console.info(`cd "${wslToWinPath(rootDir)}\\packages\\app-mobile\\android" && gradlew.bat ${apkBuildCmd}"`);
// console.info('');
// await readline('Press Enter when done:');
// apkBuildCmd = ''; // Clear the command because we've already ran it
// process.chdir(`${rnDir}/android`);
2020-11-05 18:58:23 +02:00
// apkBuildCmd = `/mnt/c/Windows/System32/cmd.exe /c "cd packages\\app-mobile\\android && gradlew.bat ${apkBuildCmd}"`;
// restoreDir = rootDir;
2020-11-05 18:58:23 +02:00
// apkBuildCmd = `/mnt/c/Windows/System32/cmd.exe /c "cd packages\\app-mobile\\android && gradlew.bat ${apkBuildCmd}"`;
2020-11-05 18:58:23 +02:00
await execCommandWithPipes('/mnt/c/Windows/System32/cmd.exe', ['/c', `cd packages\\app-mobile\\android && gradlew.bat ${apkBuildCmd}`]);
apkBuildCmd = '';
2019-06-15 10:44:34 +02:00
} else {
2019-09-19 23:51:18 +02:00
process.chdir(`${rnDir}/android`);
2020-11-06 20:45:45 +02:00
apkBuildCmd = './gradlew';
2019-06-15 10:44:34 +02:00
restoreDir = rootDir;
}
2019-06-15 19:58:09 +02:00
if (apkBuildCmd) {
2020-11-06 20:45:45 +02:00
await execCommandVerbose(apkBuildCmd, apkBuildCmdArgs);
2019-06-15 19:58:09 +02:00
}
if (restoreDir) process.chdir(restoreDir);
await fs.mkdirp(releaseDir);
2019-09-19 23:51:18 +02:00
console.info(`Copying APK to ${apkFilePath}`);
2020-11-26 21:42:05 +02:00
await fs.copy(`${rnDir}/android/app/build/outputs/apk/release/app-release.apk`, apkFilePath);
2019-07-28 18:29:07 +02:00
if (name === 'main') {
2019-09-19 23:51:18 +02:00
console.info(`Copying APK to ${releaseDir}/joplin-latest.apk`);
2020-11-26 21:42:05 +02:00
await fs.copy(`${rnDir}/android/app/build/outputs/apk/release/app-release.apk`, `${releaseDir}/joplin-latest.apk`);
2019-07-28 18:29:07 +02:00
}
for (const filename in originalContents) {
const content = originalContents[filename];
await fs.writeFile(filename, content);
}
return {
downloadUrl: downloadUrl,
apkFilename: apkFilename,
apkFilePath: apkFilePath,
};
}
async function main() {
const argv = require('yargs').argv;
if (!['release', 'prerelease'].includes(argv.type)) throw new Error('Must specify release type. Either --type=release or --type=prerelease');
await gitPullTry(false);
const isPreRelease = argv.type === 'prerelease';
if (isPreRelease) console.info('Creating pre-release');
console.info('Updating version numbers in build.gradle...');
const newContent = updateGradleConfig();
const version = gradleVersionName(newContent);
const tagName = `android-v${version}`;
2019-07-28 18:29:07 +02:00
const releaseNames = ['main', '32bit'];
2021-05-16 19:38:36 +02:00
const releaseFiles: Record<string, Release> = {};
2019-07-28 18:29:07 +02:00
for (const releaseName of releaseNames) {
releaseFiles[releaseName] = await createRelease(releaseName, tagName, version);
2019-07-28 18:29:07 +02:00
}
// NOT TESTED: These commands should not be necessary anymore since they are
// done in completeReleaseWithChangelog()
// 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']);
2019-09-19 23:51:18 +02:00
console.info(`Creating GitHub release ${tagName}...`);
const releaseOptions = { isPreRelease: isPreRelease };
2020-09-04 18:28:18 +02:00
const oauthToken = await githubOauthToken();
2020-09-04 18:28:18 +02:00
const release = await githubRelease(projectName, tagName, releaseOptions);
2019-07-28 18:36:36 +02:00
const uploadUrlTemplate = uriTemplate.parse(release.upload_url);
for (const releaseFilename in releaseFiles) {
const releaseFile = releaseFiles[releaseFilename];
const uploadUrl = uploadUrlTemplate.expand({ name: releaseFile.apkFilename });
const binaryBody = await fs.readFile(releaseFile.apkFilePath);
2019-09-19 23:51:18 +02:00
console.info(`Uploading ${releaseFile.apkFilename} to ${uploadUrl}`);
2019-07-28 18:29:07 +02:00
const uploadResponse = await fetch(uploadUrl, {
method: 'POST',
2019-07-28 18:29:07 +02:00
body: binaryBody,
headers: {
'Content-Type': 'application/vnd.android.package-archive',
2019-09-19 23:51:18 +02:00
'Authorization': `token ${oauthToken}`,
2019-07-28 18:29:07 +02:00
'Content-Length': binaryBody.length,
},
});
const uploadResponseText = await uploadResponse.text();
const uploadResponseObject = JSON.parse(uploadResponseText);
if (!uploadResponseObject || !uploadResponseObject.browser_download_url) throw new Error('Could not upload file to GitHub');
2019-07-28 18:29:07 +02:00
}
2019-09-19 23:51:18 +02:00
console.info(`Main download URL: ${releaseFiles['main'].downloadUrl}`);
2021-05-16 19:38:36 +02:00
const changelogPath = `${rootDir}/readme/changelog_android.md`;
await completeReleaseWithChangelog(changelogPath, version, tagName, 'Android', isPreRelease);
}
main().catch((error) => {
console.error('Fatal error');
console.error(error);
2018-03-23 19:32:29 +02:00
process.exit(1);
});