mirror of
https://github.com/DeviceFarmer/stf.git
synced 2026-04-17 23:53: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:
15
Dockerfile
15
Dockerfile
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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": {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user