1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-21 09:38:01 +02:00
joplin/Tools/release-android.js

205 lines
7.1 KiB
JavaScript
Raw Normal View History

const fs = require('fs-extra');
const { execCommand, githubRelease, githubOauthToken, fileExists, readline } = 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 + '/../ReactNativeClient';
const rootDir = path.dirname(__dirname);
const releaseDir = rootDir + '/_releases';
2019-06-15 19:58:09 +02:00
function wslToWinPath(wslPath) {
const s = wslPath.split('/');
if (s.length < 3) return s.join('\\');
s.splice(0, 1);
if (s[0] !== 'mnt' || s[1].length !== 1) return s.join('\\');
s.splice(0, 1);
s[0] = s[0].toUpperCase() + ':';
while (s.length && !s[s.length - 1]) s.pop();
return s.join('\\');
}
function increaseGradleVersionCode(content) {
const newContent = content.replace(/versionCode\s+(\d+)/, function(a, versionCode, c) {
const n = Number(versionCode);
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;
}
function increaseGradleVersionName(content) {
const newContent = content.replace(/(versionName\s+"\d+?\.\d+?\.)(\d+)"/, function(match, prefix, buildNum) {
const n = Number(buildNum);
if (isNaN(n) || !n) throw new Error('Invalid version code: ' + buildNum);
return prefix + (n + 1) + '"';
});
if (newContent === content) throw new Error('Could not update version name');
return newContent;
}
function updateGradleConfig() {
let content = fs.readFileSync(rnDir + '/android/app/build.gradle', 'utf8');
content = increaseGradleVersionCode(content);
content = increaseGradleVersionName(content);
fs.writeFileSync(rnDir + '/android/app/build.gradle', content);
return content;
}
function gradleVersionName(content) {
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];
}
async function createRelease(name, tagName, version) {
const originalContents = {};
2019-07-28 18:29:07 +02:00
const suffix = version + (name === 'main' ? '' : '-' + name);
console.info('Creating release: ' + suffix);
if (name === '32bit') {
let 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);
}
const apkFilename = 'joplin-v' + suffix + '.apk';
const apkFilePath = releaseDir + '/' + apkFilename;
const downloadUrl = 'https://github.com/laurent22/' + projectName + '/releases/download/' + tagName + '/' + apkFilename;
process.chdir(rootDir);
console.info('Running from: ' + process.cwd());
console.info('Building APK file v' + suffix + '...');
let restoreDir = null;
2019-06-15 19:58:09 +02:00
let apkBuildCmd = '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:
// Error: Command failed: /mnt/c/Windows/System32/cmd.exe /c "cd ReactNativeClient\android && gradlew.bat assembleRelease -PbuildDir=build"
// 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('');
console.info('cd "' + wslToWinPath(rootDir) + '\\ReactNativeClient\\android" && gradlew.bat ' + apkBuildCmd + '"');
console.info('');
2019-06-19 16:04:46 +02:00
await readline('Press Enter when done:');
2019-06-15 19:58:09 +02:00
apkBuildCmd = ''; // Clear the command because we've already ran it
2019-06-15 10:44:34 +02:00
} else {
process.chdir(rnDir + '/android');
apkBuildCmd = './gradlew ' + apkBuildCmd;
restoreDir = rootDir;
}
2019-06-15 19:58:09 +02:00
if (apkBuildCmd) {
console.info(apkBuildCmd);
const output = await execCommand(apkBuildCmd);
console.info(output);
}
if (restoreDir) process.chdir(restoreDir);
await fs.mkdirp(releaseDir);
console.info('Copying APK to ' + apkFilePath);
2018-10-02 19:19:27 +02:00
await fs.copy('ReactNativeClient/android/app/build/outputs/apk/release/app-release.apk', apkFilePath);
2019-07-28 18:29:07 +02:00
if (name === 'main') {
console.info('Copying APK to ' + releaseDir + '/joplin-latest.apk');
await fs.copy('ReactNativeClient/android/app/build/outputs/apk/release/app-release.apk', releaseDir + '/joplin-latest.apk');
}
for (let filename in originalContents) {
const content = originalContents[filename];
await fs.writeFile(filename, content);
}
return {
downloadUrl: downloadUrl,
apkFilename: apkFilename,
apkFilePath: apkFilePath,
};
}
async function main() {
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'];
const releaseFiles = {};
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
}
console.info('Updating Readme URL...');
let readmeContent = await fs.readFile('README.md', 'utf8');
2019-07-28 18:29:07 +02:00
readmeContent = readmeContent.replace(/(https:\/\/github.com\/laurent22\/joplin-android\/releases\/download\/.*?\.apk)/, releaseFiles['main'].downloadUrl);
await fs.writeFile('README.md', readmeContent);
console.info(await execCommand('git pull'));
console.info(await execCommand('git add -A'));
2018-01-19 00:43:37 +02:00
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'));
console.info('Creating GitHub release ' + tagName + '...');
const oauthToken = await githubOauthToken();
2019-07-28 18:36:36 +02:00
const release = await githubRelease(projectName, tagName);
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);
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',
'Authorization': 'token ' + oauthToken,
'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
}
console.info('Main download URL: ' + releaseFiles['main'].downloadUrl);
}
main().catch((error) => {
console.error('Fatal error');
console.error(error);
2018-03-23 19:32:29 +02:00
process.exit(1);
});