Skip to content

Commit

Permalink
Add .aab installation support (DeviceFarmer#103)
Browse files Browse the repository at this point in the history
* Add .aab installation support

Signed-off-by: nghia.viminh <[email protected]>
  • Loading branch information
nghiaviminh authored Sep 28, 2020
1 parent a0792c8 commit 841b092
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 5 deletions.
15 changes: 11 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,21 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
tar -xJf node-v*.tar.xz --strip-components 1 -C /usr/local && \
rm node-v*.tar.xz && \
su stf-build -s /bin/bash -c '/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js install' && \
apt-get -y install libzmq3-dev libprotobuf-dev git graphicsmagick yasm && \
apt-get -y install libzmq3-dev libprotobuf-dev git graphicsmagick openjdk-8-jdk yasm && \
apt-get clean && \
rm -rf /var/cache/apt/* /var/lib/apt/lists/*
rm -rf /var/cache/apt/* /var/lib/apt/lists/* && \
mkdir /tmp/bundletool && \
cd /tmp/bundletool && \
wget --progress=dot:mega \
https://github.com/google/bundletool/releases/download/1.2.0/bundletool-all-1.2.0.jar && \
mv bundletool-all-1.2.0.jar bundletool.jar

# Copy app source.
COPY . /tmp/build/

# Give permissions to our build user.
RUN mkdir -p /app && \
chown -R stf-build:stf-build /tmp/build /app
chown -R stf-build:stf-build /tmp/build /tmp/bundletool /app

# Switch over to the build user.
USER stf-build
Expand All @@ -57,8 +62,10 @@ RUN set -x && \
npm prune --production && \
mv node_modules /app && \
rm -rf ~/.node-gyp && \
mkdir /app/bundletool && \
mv /tmp/bundletool/* /app/bundletool && \
cd /app && \
rm -rf /tmp/*
find /tmp -mindepth 1 ! -regex '^/tmp/hsperfdata_root\(/.*\)?' -delete

# Switch to the app user.
USER stf
Expand Down
57 changes: 57 additions & 0 deletions lib/cli/storage-temp/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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. ' +
Expand All @@ -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
}
})
}
16 changes: 16 additions & 0 deletions lib/units/storage/temp.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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')
})
Expand All @@ -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({
Expand Down
158 changes: 158 additions & 0 deletions lib/util/bundletool.js
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
"uuid": "^3.0.0",
"ws": "^3.0.0",
"yargs": "^6.6.0",
"yauzl": "^2.10.0",
"zmq": "^2.14.0"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion res/app/components/stf/install/install-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ module.exports = function InstallService(
$rootScope.$broadcast('installation', installation)
return StorageService.storeFile('apk', $files, {
filter: function(file) {
return /\.apk$/i.test(file.name)
return /\.(apk|aab)$/i.test(file.name)
}
})
.progressed(function(e) {
Expand Down

0 comments on commit 841b092

Please sign in to comment.