Легко извлекайте информацию о версии из пакетов APK, IPA и APP

Часто, особенно при запуске автоматизированных тестов мобильных приложений, важно убедиться, что используется правильная версия приложения. В этой статье описаны простые способы запроса информации о версии из пакетов приложений Android и iOS с помощью инструментов командной строки или библиотек Java. Оба подхода можно легко интегрировать в конвейер сборки.

Командная строка

Для Android один из подходов к синтаксическому анализу метаинформации из файла APK - это использовать aapt примерно так:

aapt dump badging /path/test.apk

Это печатает много информации, поэтому нам нужно обрезать вывод до того, что нам интересно:

aapt dump badging test.apk | grep -o 'versionName=[^,]*' | cut -d'=' -f 2 | cut -d ' ' -f 1 | tr -d "'"

Точно так же мы можем запросить код версии (или любую другую информацию, если на то пошло):

aapt dump badging /path/test.apk | grep -o ‘versionCode=[^,]*’ | cut -d’=’ -f 2 | cut -d ‘ ‘ -f 1 | tr -d "'"

Если у нас есть приложение, установленное на устройстве (физическом или симуляторе), которое подключено через adb, мы также можем запросить версию следующим образом:

adb shell dumpsys package package.of.the.app | grep versionName | cut -d'=' -f 2 | cut -d ' ' -f 1
adb shell dumpsys package package.of.the.app | grep versionCode | cut -d'=' -f 2 | cut -d ' ' -f 1

Для приложений iOS давайте сначала рассмотрим пакеты APP (используемые для симуляторов). Если у нас Mac OSX, мы можем использовать defaults. Обратите внимание, что путь должен быть абсолютным:

defaults read /path/test.app/Info CFBundleShortVersionString
defaults read /path/test.app/Info CFBundleVersion

В Linux нам нужен plistutil и синтаксический анализатор XML, например xmllint:

plistutil -i /path/test.app/Info | xmllint --xpath "//key[text()='CFBundleShortVersionString']/following-sibling::string[1]/text()" -
plistutil -i /path/test.app/Info | xmllint --xpath "//key[text()='CFBundleVersion']/following-sibling::string[1]/text()"

Если мы хотим получить информацию о версии из файла IPA (используемого для физических устройств), мы должны сначала его распаковать. Затем мы можем запустить те же команды, что и выше:

# Unpack IPA file
unzip -q /path/test.ipa -d /tmp/ipa
# Mac OS
defaults read /tmp/ipa/Payload/test.app/Info CFBundleShortVersionString
defaults read /tmp/ipa/Payload/test.app/Info CFBundleVersion
# Linux
plistutil -i /tmp/ipa/Payload/test.app/Info | xmllint --xpath "//key[text()='CFBundleShortVersionString']/following-sibling::string[1]/text()" -
plistutil -i /tmp/ipa/Payload/test.app/Info | xmllint --xpath "//key[text()='CFBundleVersion']/following-sibling::string[1]/text()"
# Clean-up
rm -rf /tmp/ipa

Путь Java

Один из способов получить информацию о версии из программы Java - использовать Runtime или ProcessBuilder для выполнения вышеуказанных команд оболочки. Однако это зависит от платформы и немного хрупко. Для этого необходимо, чтобы используемые инструменты (aapt, plistutil, xmllint и т. Д.) Были доступны на машине, на которой выполняется код Java.

Поэтому предпочтительнее собственное решение Java. К счастью, есть библиотеки с открытым исходным кодом для всех наших нужд:

  • Apk-parser: эта библиотека позволяет нам читать информацию из файлов APK.
  • Dd-plist: эта библиотека может обрабатывать plist файлов, используемых для хранения метаинформации приложения iOS.
  • Zip4j: эта библиотека - один из многих вариантов работы с zip-файлами в Java.

Используя Maven, мы можем включить их в наш проект:

<properties>
  <dd.plist.version>1.21</dd.plist.version>
  <apk.parser.version>2.6.4</apk.parser.version>
  <zip4j.version>1.3.2</zip4j.version>
</properties>
<dependencies>
  <dependency>
    <groupId>com.googlecode.plist</groupId>
    <artifactId>dd-plist</artifactId>
    <version>${dd.plist.version}</version>
  </dependency>
  <dependency>
    <groupId>net.dongliu</groupId>
    <artifactId>apk-parser</artifactId>
    <version>${apk.parser.version}</version>
  </dependency>
  <dependency>
    <groupId>net.lingala.zip4j</groupId>
    <artifactId>zip4j</artifactId>
    <version>${zip4j.version}</version>
  </dependency>
</depdencies>

Для Android мы можем просто получить информацию о версии следующим образом:

String appPath = "/path/test.apk";
ApkFile apkFile = new ApkFile(new File(appPath));
ApkMeta apkMeta = apkFile.getApkMeta();
String versionName = apkMeta.getVersionName();
String versionCode = apkMeta.getVersionCode().toString();

Для iOS код пакета APP выглядит следующим образом:

String appPath = "/path/test.app";
NSDictionary dictionary = (NSDictionary) PropertyListParser.parse(path);
String versionName = dictionary.objectForKey(“CFBundleShortVersionString”).toString();
String versionCode = dictionary.objectForKey(“CFBundleVersion”).toString();

Если вместо этого мы имеем дело с файлом IPA:

String appPath = "/path/test.ipa";
# Unpack IPA file
String tmpFolder = File.separator + "tmp" + File.separator + UUID.randomUUID().toString();
ZipFile zipFile = new ZipFile(appPath);
zipFile.extractAll(tmpFolder);
# Fetch version information
NSDictionary dictionary = (NSDictionary) PropertyListParser.parse(new File(new File(tmpFolder + File.separator + "Payload").listFiles()[0].getAbsolutePath() + File.separator + "Info.plist"));
String versionName = dictionary.objectForKey(“CFBundleShortVersionString”).toString();
String versionCode = dictionary.objectForKey(“CFBundleVersion”).toString();
# Clean-up
Files.walk(Paths.get(tmpFolder)).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);

Для простоты обработка исключений была удалена из приведенных выше примеров.

Полный код доступен здесь.

Универсальная библиотека

Приведенная выше демонстрация является частью justtestlah тестовой среды. Вы можете использовать mobile-tools JAR (который поставляется с минимальным количеством дополнительных зависимостей) для запроса версии и другой метаинформации из приложений Android и iOS. Импортируйте его следующим образом:

<properties>
  <justtestlah.version>1.3.2</justtestlah.version>
</properties>
<dependencies>
  <dependency>
    <groupId>io.github.martinschneider</groupId>
    <artifactId>justtestlah-mobile-tools</artifactId>
    <version>${justtestlah.version}</version>
  </dependency>
</depdencies>

Затем просто используйте это так:

ApplicationInfo appInfo = new ApplicationInfoService().getAppInfo(appPath);
String versionName = appInfo.getVersionName();
String versionCode = appInfo.getVersionCode();
String applicationName = appInfo.getApplicationName();

appPath может указывать на любой файл APK, IPA или APP.