appium-adb
Version:
Android Debug Bridge interface
146 lines • 6.55 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractUniversalApk = extractUniversalApk;
const logger_1 = require("../logger");
const node_path_1 = __importDefault(require("node:path"));
const support_1 = require("@appium/support");
const lru_cache_1 = require("lru-cache");
const helpers_1 = require("../helpers");
const async_lock_1 = __importDefault(require("async-lock"));
const bluebird_1 = __importDefault(require("bluebird"));
const node_crypto_1 = __importDefault(require("node:crypto"));
const AAB_CACHE = new lru_cache_1.LRUCache({
max: 10,
dispose: (extractedFilesRoot) => support_1.fs.rimraf(extractedFilesRoot),
});
const AAB_CACHE_GUARD = new async_lock_1.default();
const UNIVERSAL_APK = 'universal.apk';
process.on('exit', () => {
if (!AAB_CACHE.size) {
return;
}
const paths = [...AAB_CACHE.values()];
logger_1.log.debug(`Performing cleanup of ${paths.length} cached .aab ` + support_1.util.pluralize('package', paths.length));
for (const appPath of paths) {
try {
// Asynchronous calls are not supported in onExit handler
support_1.fs.rimrafSync(appPath);
}
catch (e) {
logger_1.log.warn(e.message);
}
}
});
/**
* Builds a universal .apk from the given .aab package. See
* https://developer.android.com/studio/command-line/bundletool#generate_apks
* for more details.
*
* @param aabPath Full path to the source .aab package
* @param opts Options for APK creation
* @returns The path to the resulting universal .apk. The .apk is stored in the internal cache
* by default.
* @throws {Error} If there was an error while creating the universal .apk
*/
async function extractUniversalApk(aabPath, opts = {}) {
if (!(await support_1.fs.exists(aabPath))) {
throw new Error(`The file at '${aabPath}' either does not exist or is not accessible`);
}
const aabName = node_path_1.default.basename(aabPath);
const apkName = aabName.substring(0, aabName.length - node_path_1.default.extname(aabName).length) + '.apk';
const tmpRoot = await support_1.tempDir.openDir();
const tmpApksPath = node_path_1.default.join(tmpRoot, `${aabName}.apks`);
try {
return await AAB_CACHE_GUARD.acquire(aabPath, async () => {
const aabHash = await support_1.fs.hash(aabPath);
const { keystore, keystorePassword, keyAlias, keyPassword } = opts;
let cacheHash = aabHash;
if (keystore) {
if (!(await support_1.fs.exists(keystore))) {
throw new Error(`The keystore file at '${keystore}' either does not exist ` + `or is not accessible`);
}
if (!keystorePassword || !keyAlias || !keyPassword) {
throw new Error('It is mandatory to also provide keystore password, key alias, ' +
'and key password if the keystore path is set');
}
const keystoreHash = await support_1.fs.hash(keystore);
const keyAliasHash = node_crypto_1.default.createHash('sha1');
keyAliasHash.update(keyAlias);
cacheHash = [cacheHash, keystoreHash, keyAliasHash.digest('hex')].join(':');
}
logger_1.log.debug(`Calculated the cache key for '${aabPath}': ${cacheHash}`);
if (AAB_CACHE.has(cacheHash)) {
const cachedRoot = AAB_CACHE.get(cacheHash);
if (cachedRoot) {
const resultPath = node_path_1.default.resolve(cachedRoot, apkName);
if (await support_1.fs.exists(resultPath)) {
return resultPath;
}
}
AAB_CACHE.delete(cacheHash);
}
await this.initAapt2();
const binaries = this.binaries;
const args = [
'build-apks',
'--aapt2',
binaries.aapt2,
'--bundle',
aabPath,
'--output',
tmpApksPath,
...(keystore
? [
'--ks',
keystore,
'--ks-pass',
`pass:${keystorePassword}`,
'--ks-key-alias',
keyAlias,
'--key-pass',
`pass:${keyPassword}`,
]
: []),
'--mode=universal',
];
logger_1.log.debug(`Preparing universal .apks bundle from '${aabPath}'`);
await this.execBundletool(args, `Cannot build a universal .apks bundle from '${aabPath}'`);
logger_1.log.debug(`Unpacking universal application bundle at '${tmpApksPath}' to '${tmpRoot}'`);
await (0, helpers_1.unzipFile)(tmpApksPath, tmpRoot);
let universalApkPath;
const fileDeletionPromises = [];
const allFileNames = await support_1.fs.readdir(tmpRoot);
for (const fileName of allFileNames) {
const fullPath = node_path_1.default.join(tmpRoot, fileName);
if (fileName === UNIVERSAL_APK) {
universalApkPath = fullPath;
}
else {
fileDeletionPromises.push(support_1.fs.rimraf(fullPath));
}
}
try {
await bluebird_1.default.all(fileDeletionPromises);
}
catch { }
if (!universalApkPath) {
logger_1.log.debug(`The following items were extracted from the .aab bundle: ${allFileNames}`);
throw new Error(`${UNIVERSAL_APK} cannot be found in '${aabPath}' bundle. ` +
`Does the archive contain a valid application bundle?`);
}
const resultPath = node_path_1.default.join(tmpRoot, apkName);
logger_1.log.debug(`Found ${UNIVERSAL_APK} at '${universalApkPath}'. Caching it to '${resultPath}'`);
await support_1.fs.mv(universalApkPath, resultPath);
AAB_CACHE.set(cacheHash, tmpRoot);
return resultPath;
});
}
catch (e) {
await support_1.fs.rimraf(tmpRoot);
throw e;
}
}
//# sourceMappingURL=aab-utils.js.map