mirror of
https://github.com/DeviceFarmer/stf.git
synced 2026-04-19 00:23:25 +02:00
Add .aab installation support (#103)
* Add .aab installation support Signed-off-by: nghia.viminh <nghia.viminh@gameloft.com>
This commit is contained in:
@@ -25,6 +25,52 @@ module.exports.builder = function(yargs) {
|
||||
, type: 'string'
|
||||
, default: os.tmpdir()
|
||||
})
|
||||
.option('bundletool-path', {
|
||||
describe: 'The path to bundletool binary.'
|
||||
, type: 'string'
|
||||
, default: '/app/bundletool/bundletool.jar'
|
||||
})
|
||||
.option('ks', {
|
||||
describe: 'The name of the keystore to sign APKs built from AAB.'
|
||||
, type: 'string'
|
||||
, default: 'openstf'
|
||||
})
|
||||
.option('ks-key-alias', {
|
||||
describe: 'Indicates the alias to be used in the future to refer to the keystore.'
|
||||
, type: 'string'
|
||||
, default: 'mykey'
|
||||
})
|
||||
.option('ks-pass', {
|
||||
describe: 'The password of the keystore.'
|
||||
, type: 'string'
|
||||
, default: 'openstf'
|
||||
})
|
||||
.option('ks-key-pass', {
|
||||
describe: 'The password of the private key contained in keystore.'
|
||||
, type: 'string'
|
||||
, default: 'openstf'
|
||||
})
|
||||
.option('ks-keyalg', {
|
||||
describe: 'The algorithm that is used to generate the key.'
|
||||
, type: 'string'
|
||||
, default: 'RSA'
|
||||
})
|
||||
.option('ks-validity', {
|
||||
describe: 'Number of days of keystore validity.'
|
||||
, type: 'number'
|
||||
, default: '90'
|
||||
})
|
||||
.option('ks-keysize', {
|
||||
describe: 'Key size of the keystore.'
|
||||
, type: 'number'
|
||||
, default: '2048'
|
||||
})
|
||||
.option('ks-dname', {
|
||||
describe: 'Keystore Distinguished Name, contain Common Name(CN), ' +
|
||||
'Organizational Unit (OU), Oranization(O), Locality (L), State (S) and Country (C).'
|
||||
, type: 'string'
|
||||
, default: 'CN=openstf.io, OU=openstf, O=openstf, L=PaloAlto, S=California, C=US'
|
||||
})
|
||||
.epilog('Each option can be be overwritten with an environment variable ' +
|
||||
'by converting the option to uppercase, replacing dashes with ' +
|
||||
'underscores and prefixing it with `STF_STORAGE_TEMP_` (e.g. ' +
|
||||
@@ -36,5 +82,16 @@ module.exports.handler = function(argv) {
|
||||
port: argv.port
|
||||
, saveDir: argv.saveDir
|
||||
, maxFileSize: argv.maxFileSize
|
||||
, bundletoolPath: argv.bundletoolPath
|
||||
, keystore: {
|
||||
ksPath: `/tmp/${argv.ks}.keystore`
|
||||
, ksKeyAlias: argv.ksKeyAlias
|
||||
, ksPass: argv.ksPass
|
||||
, ksKeyPass: argv.ksKeyPass
|
||||
, ksKeyalg: argv.ksKeyalg
|
||||
, ksValidity: argv.ksValidity
|
||||
, ksKeysize: argv.ksKeysize
|
||||
, ksDname: argv.ksDname
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ var logger = require('../../util/logger')
|
||||
var Storage = require('../../util/storage')
|
||||
var requtil = require('../../util/requtil')
|
||||
var download = require('../../util/download')
|
||||
var bundletool = require('../../util/bundletool')
|
||||
|
||||
module.exports = function(options) {
|
||||
var log = logger.createLogger('storage:temp')
|
||||
@@ -91,6 +92,9 @@ module.exports = function(options) {
|
||||
form.uploadDir = options.saveDir
|
||||
}
|
||||
form.on('fileBegin', function(name, file) {
|
||||
if (/\.aab$/.test(file.name)) {
|
||||
file.isAab = true
|
||||
}
|
||||
var md5 = crypto.createHash('md5')
|
||||
file.name = md5.update(file.name).digest('hex')
|
||||
})
|
||||
@@ -103,9 +107,21 @@ module.exports = function(options) {
|
||||
field: field
|
||||
, id: storage.store(file)
|
||||
, name: file.name
|
||||
, path: file.path
|
||||
, isAab: file.isAab
|
||||
}
|
||||
})
|
||||
})
|
||||
.then(function(storedFiles) {
|
||||
return Promise.all(storedFiles.map(function(file) {
|
||||
return bundletool({
|
||||
bundletoolPath: options.bundletoolPath
|
||||
, keystore: options.keystore
|
||||
, file: file
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
.then(function(storedFiles) {
|
||||
res.status(201)
|
||||
.json({
|
||||
|
||||
158
lib/util/bundletool.js
Normal file
158
lib/util/bundletool.js
Normal file
@@ -0,0 +1,158 @@
|
||||
var cp = require('child_process')
|
||||
var fs = require('fs')
|
||||
var path = require('path')
|
||||
var request = require('request')
|
||||
|
||||
var Promise = require('bluebird')
|
||||
var yauzl = require('yauzl')
|
||||
|
||||
var logger = require('./logger')
|
||||
|
||||
module.exports = function(options) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var log = logger.createLogger('util:bundletool')
|
||||
var bundletoolFilePath = options.bundletoolPath
|
||||
var bundle = options.file
|
||||
var bundlePath = bundle.path
|
||||
var outputPath = bundlePath + '.apks'
|
||||
var keystore = options.keystore
|
||||
|
||||
function checkIfJava() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var check = cp.spawn('java', ['-version'])
|
||||
var stderrChunks = []
|
||||
check.on('error', function(err) {
|
||||
reject(err)
|
||||
})
|
||||
check.stderr.on('data', function(data) {
|
||||
stderrChunks = stderrChunks.concat(data)
|
||||
})
|
||||
check.stderr.on('end', function() {
|
||||
var data = Buffer.concat(stderrChunks).toString().split('\n')[0]
|
||||
var regex = new RegExp('(openjdk|java) version')
|
||||
var javaVersion = regex.test(data) ? data.split(' ')[2].replace(/"/g, '') : false
|
||||
if (javaVersion !== false) {
|
||||
resolve(javaVersion)
|
||||
}
|
||||
else {
|
||||
reject(new Error('Java not found'), null)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function convert() {
|
||||
var proc = cp.spawn('java', [
|
||||
'-jar'
|
||||
, bundletoolFilePath
|
||||
, 'build-apks'
|
||||
, `--bundle=${bundlePath}`
|
||||
, `--output=${outputPath}`
|
||||
, `--ks=${keystore.ksPath}`
|
||||
, `--ks-pass=pass:${keystore.ksPass}`
|
||||
, `--ks-key-alias=${keystore.ksKeyAlias}`
|
||||
, `--key-pass=pass:${keystore.ksKeyPass}`
|
||||
, '--overwrite'
|
||||
, '--mode=universal'
|
||||
])
|
||||
|
||||
proc.on('error', function(err) {
|
||||
reject(err)
|
||||
})
|
||||
|
||||
proc.on('exit', function(code, signal) {
|
||||
if (signal) {
|
||||
reject(new Error('Exited with signal ' + signal))
|
||||
}
|
||||
else if (code === 0) {
|
||||
yauzl.open(outputPath, {lazyEntries: true}, function(err, zipfile) {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
zipfile.readEntry()
|
||||
zipfile.on('entry', function(entry) {
|
||||
if (/\/$/.test(entry.fileName)) {
|
||||
zipfile.readEntry()
|
||||
}
|
||||
else {
|
||||
zipfile.openReadStream(entry, function(err, readStream) {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
readStream.on('end', function() {
|
||||
zipfile.readEntry()
|
||||
})
|
||||
var filePath = entry.fileName.split('/')
|
||||
var fileName = filePath[filePath.length - 1]
|
||||
var writeStream = fs.createWriteStream(path.join('/tmp/', fileName))
|
||||
writeStream.on('error', function(err) {
|
||||
reject(err)
|
||||
})
|
||||
readStream.pipe(writeStream)
|
||||
})
|
||||
}
|
||||
})
|
||||
zipfile.on('error', function(err) {
|
||||
reject(err)
|
||||
})
|
||||
zipfile.once('end', function() {
|
||||
fs.renameSync('/tmp/universal.apk', bundlePath)
|
||||
fs.readdirSync('/tmp/', function(err, files) {
|
||||
if (err) {
|
||||
reject(err)
|
||||
}
|
||||
for (var file of files) {
|
||||
fs.unlinkSync(path.resolve('/tmp/', file))
|
||||
}
|
||||
fs.unlinkSync(outputPath)
|
||||
})
|
||||
log.info('AAB -> APK')
|
||||
resolve(bundle)
|
||||
})
|
||||
})
|
||||
}
|
||||
else {
|
||||
reject(new Error('Exited with status ' + code))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (bundle.isAab === true) {
|
||||
log.info('AAB detected')
|
||||
checkIfJava()
|
||||
.then(function() {
|
||||
if (!fs.existsSync(keystore.ksPath)) {
|
||||
cp.spawnSync('keytool', [
|
||||
'-genkey'
|
||||
, '-noprompt'
|
||||
, '-keystore', keystore.ksPath
|
||||
, '-alias', keystore.ksKeyAlias
|
||||
, '-keyalg', keystore.ksKeyalg
|
||||
, '-keysize', keystore.ksKeysize
|
||||
, '-storepass', keystore.ksPass
|
||||
, '-keypass', keystore.ksKeyPass
|
||||
, '-dname', keystore.ksDname
|
||||
, '-validity', keystore.ksValidity
|
||||
])
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
if(!fs.existsSync(keystore.ksPath)) {
|
||||
reject('Keystore not found')
|
||||
}
|
||||
else if(!fs.existsSync(bundletoolFilePath)) {
|
||||
reject('bundletool not found')
|
||||
}
|
||||
else {
|
||||
convert()
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
reject(err)
|
||||
})
|
||||
}
|
||||
else {
|
||||
resolve(bundle)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user