Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ debug/
release/
testresults/
.eslintcache
cordova/bundle.keystore

# OSX
.DS_store
Expand Down
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ stages:
displayName: 'Install Node.js 10.16.3'
- script: yarn install
displayName: 'Run yarn install'
- script: yarn gulp release --android
- script: yarn gulp debug-release --android
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now we need to have both here, debug and non debug build like in Windows:

    - script: yarn gulp release --android
      displayName: 'Run yarn release for android'
      condition: and(succeeded(), eq('${{ parameters.releaseBuild }}', true))
    - script: yarn gulp debug-release --android
      displayName: 'Run yarn debug release for android'
      condition: and(succeeded(), eq('${{ parameters.releaseBuild }}', false))

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that part of the build of the android application bundle for the Google Play store is to sign the bundle with a private key. This cannot easily moved to azure without compromising the key, so I have opted to be doing this on a physical machine for now.
There are some more constraints with the Google Play store around versioning that make it hard to fully automate builds to be published in this way, so doing this manually and locally is probably best anyway.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don' t understand exactly... I understand the part that we need a private key, but I don't understand why we can't have the two options (release and debug-release) like in windows. I understand that by default it will execute the debug-release anyway.
I suppose that what you say is that the release will not work because it simply does not have the private key so is better to not put the option here?
If that is the reason, then what we do is to use different private keys for the "official" release and the "official" nightly, what does not seem very clean. The user will need to remove one to install the other. There must be a way to use, in a safe way, the same private key for both versions. Another thing is a local build of the developer, in this case of course the private key will be different only for testing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@McGiverGim: Not sure what you mean with your statements about using different private keys - this is not how the Google Play store works. There will never be a way to install the nightlies through the Play store, so users will always have to overrride security and allow the installation of untrusted packages.
Actually, one thing that this pull request proposes is to use a different package id (com.betaflight.betaflight_configurator_debug vs. com.betaflight.betaflight_configurator) for the nightly / debug builds. Since this is used as the unique identifier for packages in android it will be possible to have the official Play store version and a debug version installed in parallel.

displayName: 'Run yarn release for android'
- task: PublishPipelineArtifact@1
displayName: 'Publish Android release'
Expand Down
10 changes: 0 additions & 10 deletions cordova/build.json

This file was deleted.

18 changes: 18 additions & 0 deletions cordova/build_template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"android": {
"debug": {
"keystore": "debug.keystore",
"storePassword": "betaflight_debug",
"alias": "betaflight_debug",
"password": "password",
"packageType": "apk"
},
"release": {
"keystore": "bundle.keystore",
"storePassword": "[INJECTED_BY_GULPFILE]",
"alias": "betaflight",
"password": "password",
"packageType": "bundle"
}
}
}
1 change: 1 addition & 0 deletions cordova/config_template.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
<preference name="SplashMaintainAspectRatio" value="true"/>
<preference name="SplashShowOnlyFirstTime" value="true"/>
<preference name="android-minSdkVersion" value="19"/>
<preference name="android-targetSdkVersion" value="30" />
<config-file parent="/manifest/application/activity" target="AndroidManifest.xml">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
Expand Down
Binary file added cordova/debug.keystore
Binary file not shown.
Binary file removed cordova/release.keystore
Binary file not shown.
179 changes: 138 additions & 41 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ const os = require('os');
const git = require('simple-git')();
const source = require('vinyl-source-stream');
const stream = require('stream');
const prompt = require('gulp-prompt');

const cordova = require("cordova-lib").cordova;

const browserify = require('browserify');
const glob = require('glob');

Expand All @@ -45,7 +47,6 @@ const NODE_ENV = process.env.NODE_ENV || 'production';

const NAME_REGEX = /-/g;


const nwBuilderOptions = {
version: '0.54.1',
files: `${DIST_DIR}**/*`,
Expand Down Expand Up @@ -110,18 +111,18 @@ const debugDistBuild = gulp.series(process_package_debug, dist_src, dist_changel
const distRebuild = gulp.series(clean_dist, distBuild);
gulp.task('dist', distRebuild);

const appsBuild = gulp.series(gulp.parallel(clean_apps, distRebuild), apps, gulp.series(cordova_apps()), gulp.parallel(listPostBuildTasks(APPS_DIR)));
const appsBuild = gulp.series(gulp.parallel(clean_apps, distRebuild), apps, gulp.series(cordova_apps(true)), gulp.parallel(listPostBuildTasks(APPS_DIR)));
gulp.task('apps', appsBuild);

const debugAppsBuild = gulp.series(gulp.parallel(clean_debug, gulp.series(clean_dist, debugDistBuild)), debug, gulp.series(cordova_apps()), gulp.parallel(listPostBuildTasks(DEBUG_DIR)));
const debugAppsBuild = gulp.series(gulp.parallel(clean_debug, gulp.series(clean_dist, debugDistBuild)), debug, gulp.series(cordova_apps(false)), gulp.parallel(listPostBuildTasks(DEBUG_DIR)));

const debugBuild = gulp.series(debugDistBuild, debug, gulp.parallel(listPostBuildTasks(DEBUG_DIR)), start_debug);
gulp.task('debug', debugBuild);

const releaseBuild = gulp.series(gulp.parallel(clean_release, appsBuild), gulp.parallel(listReleaseTasks(APPS_DIR)));
const releaseBuild = gulp.series(gulp.parallel(clean_release, appsBuild), gulp.parallel(listReleaseTasks(true, APPS_DIR)));
gulp.task('release', releaseBuild);

const debugReleaseBuild = gulp.series(gulp.parallel(clean_release, debugAppsBuild), gulp.parallel(listReleaseTasks(DEBUG_DIR)));
const debugReleaseBuild = gulp.series(gulp.parallel(clean_release, debugAppsBuild), gulp.parallel(listReleaseTasks(false, DEBUG_DIR)));
gulp.task('debug-release', debugReleaseBuild);

gulp.task('default', debugBuild);
Expand Down Expand Up @@ -292,21 +293,48 @@ function processPackage(done, gitRevision, isReleaseBuild) {
metadata.packageId = pkg.name;
}

const packageJson = new stream.Readable;
packageJson.push(JSON.stringify(pkg, undefined, 2));
packageJson.push(null);
function version_prompt() {
return gulp.src('.')
.pipe(prompt.prompt([{
type: 'input',
name: 'version',
message: `Package version (default: ${pkg.version}):`,
}, {
type: 'input',
name: 'storeVersion',
message: 'Google Play store version (<x.y.z>, default: package version):',
}], function(res) {
if (res.version) {
pkg.version = res.version;
}
if (res.storeVersion) {
metadata.storeVersion = res.storeVersion;
}
}));
}

Object.keys(pkg)
.filter(key => metadataKeys.includes(key))
.forEach((key) => {
metadata[key] = pkg[key];
});
function write_package_file() {
Object.keys(pkg)
.filter(key => metadataKeys.includes(key))
.forEach((key) => {
metadata[key] = pkg[key];
});

packageJson
.pipe(source('package.json'))
.pipe(gulp.dest(DIST_DIR));
const packageJson = new stream.Readable;
packageJson.push(JSON.stringify(pkg, undefined, 2));
packageJson.push(null);

done();
return packageJson
.pipe(source('package.json'))
.pipe(gulp.dest(DIST_DIR));
}

const platforms = getPlatforms();
if (platforms.indexOf('android') !== -1 && isReleaseBuild) {
gulp.series(version_prompt, write_package_file)(done);
} else {
gulp.series(write_package_file)(done);
}
}

function dist_src() {
Expand Down Expand Up @@ -465,7 +493,7 @@ function debug(done) {
buildNWAppsWrapper(platforms, 'sdk', DEBUG_DIR, done);
}

function injectARMCache(flavor, callback) {
function injectARMCache(flavor, done) {
const flavorPostfix = `-${flavor}`;
const flavorDownloadPostfix = flavor !== 'normal' ? `-${flavor}` : '';
clean_cache().then(function() {
Expand Down Expand Up @@ -528,7 +556,7 @@ function injectARMCache(flavor, callback) {
clean_debug();
process.exit(1);
}
callback();
done();
}
);
}
Expand Down Expand Up @@ -592,10 +620,10 @@ function buildNWApps(platforms, flavor, dir, done) {

function getGitRevision(done, callback, isReleaseBuild) {
let gitRevision = 'norevision';
git.diff([ '--shortstat' ], function (err, diff) {
if (!err && !diff) {
git.log([ '-1', '--pretty=format:%h' ], function (err, rev) {
if (!err) {
git.diff([ '--shortstat' ], function (err1, diff) {
if (!err1 && !diff) {
git.log([ '-1', '--pretty=format:%h' ], function (err2, rev) {
if (!err2) {
gitRevision = rev.latest.hash;
}

Expand All @@ -608,7 +636,6 @@ function getGitRevision(done, callback, isReleaseBuild) {
}

function start_debug(done) {

const platforms = getPlatforms();

if (platforms.length === 1) {
Expand Down Expand Up @@ -813,7 +840,7 @@ function createDirIfNotExists(dir) {
}

// Create a list of the gulp tasks to execute for release
function listReleaseTasks(appDirectory) {
function listReleaseTasks(isReleaseBuild, appDirectory) {

const platforms = getPlatforms();

Expand Down Expand Up @@ -869,7 +896,11 @@ function listReleaseTasks(appDirectory) {

if (platforms.indexOf('android') !== -1) {
releaseTasks.push(function release_android() {
return cordova_release();
if (isReleaseBuild) {
return cordova_release();
} else {
return cordova_debug_release();
}
});
}

Expand All @@ -891,6 +922,7 @@ function cordova_dist() {
distTasks.push(cordova_packagejson);
distTasks.push(cordova_manifestjson);
distTasks.push(cordova_configxml);
distTasks.push(cordova_rename_build_json);
distTasks.push(cordova_browserify);
distTasks.push(cordova_depedencies);
if (cordovaDependencies) {
Expand All @@ -903,11 +935,16 @@ function cordova_dist() {
}
return distTasks;
}
function cordova_apps() {

function cordova_apps(isReleaseBuild) {
const appsTasks = [];
const platforms = getPlatforms();
if (platforms.indexOf('android') !== -1) {
appsTasks.push(cordova_build);
if (isReleaseBuild) {
appsTasks.push(cordova_build);
} else {
appsTasks.push(cordova_debug_build);
}
} else {
appsTasks.push(function cordova_dist_none(done) {
done();
Expand All @@ -933,7 +970,6 @@ function cordova_copy_www() {
}

function cordova_resources() {

return gulp.src('assets/android/**')
.pipe(gulp.dest(`${CORDOVA_DIST_DIR}resources/android/`));
}
Expand All @@ -947,7 +983,7 @@ function cordova_include_www() {
}

function cordova_copy_src() {
return gulp.src([`${CORDOVA_DIR}**`, `!${CORDOVA_DIR}config_template.xml`, `!${CORDOVA_DIR}package_template.json`])
return gulp.src([`${CORDOVA_DIR}**`, `!${CORDOVA_DIR}config_template.xml`, `!${CORDOVA_DIR}package_template.json`, `!${CORDOVA_DIR}build_template.json`])
.pipe(gulp.dest(`${CORDOVA_DIST_DIR}`));
}

Expand All @@ -972,6 +1008,8 @@ function cordova_packagejson() {
'author': metadata.author,
'license': metadata.license,
}))
.pipe(gulp.dest(CORDOVA_DIST_DIR))
.pipe(rename('manifest.json'))
.pipe(gulp.dest(CORDOVA_DIST_DIR));
}

Expand All @@ -984,7 +1022,7 @@ function cordova_manifestjson() {
}

function cordova_configxml() {
let androidName = metadata.packageId.replace(NAME_REGEX, '_');
const androidName = metadata.packageId.replace(NAME_REGEX, '_');

return gulp.src([`${CORDOVA_DIST_DIR}config.xml`])
.pipe(xmlTransformer([
Expand All @@ -994,12 +1032,18 @@ function cordova_configxml() {
], 'http://www.w3.org/ns/widgets'))
.pipe(xmlTransformer([
{ path: '.', attr: { 'id': `com.betaflight.${androidName}` } },
{ path: '.', attr: { 'version': metadata.version } },
{ path: '.', attr: { 'version': metadata.storeVersion ? metadata.storeVersion : metadata.version } },
]))
.pipe(gulp.dest(CORDOVA_DIST_DIR));
}

function cordova_browserify(callback) {
function cordova_rename_build_json() {
return gulp.src(`${CORDOVA_DIR}build_template.json`)
.pipe(rename('build.json'))
.pipe(gulp.dest(CORDOVA_DIST_DIR));
}

function cordova_browserify(done) {
const readFile = function(file) {
return new Promise(function(resolve) {
if (!file.includes("node_modules")) {
Expand All @@ -1017,7 +1061,7 @@ function cordova_browserify(callback) {
glob(`${CORDOVA_DIST_DIR}www/**/*.js`, {}, function (err, files) {
const readLoop = function() {
if (files.length === 0) {
callback();
done();
} else {
const file = files.pop();
readFile(file).then(function() {
Expand Down Expand Up @@ -1056,24 +1100,77 @@ function cordova_debug() {
cordova.run();
}

function cordova_build(cb) {
function cordova_debug_build(done) {
cordova.build({
'platforms': ['android'],
'options': {
release: true,
release: false,
buildConfig: 'build.json',
},
}).then(function() {
process.chdir('../');
cb();

console.log(`APK has been generated at ${CORDOVA_DIST_DIR}platforms/android/app/build/outputs/apk/release/app-release.apk`);

done();
});
console.log(`APK will be generated at ${CORDOVA_DIST_DIR}platforms/android/app/build/outputs/apk/release/app-release.apk`);
}

async function cordova_release() {
const filename = await getReleaseFilename('android', 'apk');
function cordova_build(done) {
let storePassword = '';
return gulp.series(function password_prompt() {
return gulp.src('.')
.pipe(prompt.prompt({
type: 'password',
name: 'storePassword',
message: 'Please enter the keystore password:',
}, function(res) {
storePassword = res.storePassword;
}));
}, function set_password() {
return gulp.src(`build.json`)
.pipe(jeditor({
'android': {
'release' : {
'storePassword': storePassword,
},
},
}))
.pipe(gulp.dest('./'));
}, function build(done2) {
return cordova.build({
'platforms': ['android'],
'options': {
release: true,
buildConfig: 'build.json',
},
}).then(function() {
// Delete the file containing the store password
del(['build.json'], { force: true });
process.chdir('../');

console.log('AAB has been generated at dist_cordova/platforms/android/app/build/outputs/bundle/release/app.aab');
done2();
});
})(done);
}

async function cordova_debug_release() {
const filename = getReleaseFilename('android', 'apk');

console.log(`Release APK : release/${filename}`);
return gulp.src(`${CORDOVA_DIST_DIR}platforms/android/app/build/outputs/apk/release/app-release.apk`)

return gulp.src(`${CORDOVA_DIST_DIR}platforms/android/app/build/outputs/apk/debug/app-debug.apk`)
.pipe(rename(filename))
.pipe(gulp.dest(RELEASE_DIR));
}

async function cordova_release() {
const filename = getReleaseFilename('android', 'aab');

console.log(`Release AAB : release/${filename}`);

return gulp.src(`${CORDOVA_DIST_DIR}platforms/android/app/build/outputs/bundle/release/app.aab`)
.pipe(rename(filename))
.pipe(gulp.dest(RELEASE_DIR));
}
Loading