Pull to refresh

Генерация версии android приложения из ревизии subversion и git

Reading time 6 min
Views 3.8K
Когда пользователи сталкиваются с проблемами — всегда хочется точно знать какой именно версией ПО они пользуются. При использовании системы контроля версий и автоматической нумерации версий ПО, такую информацию можно предоставить пользователям, а в случае необходимости просто попросить продиктовать строку.

О том, как можно пронумеровать свой android проект написано здесь и здесь. В обоих статьях рассмотрен пример получения версии проекта с помощью 'svn info', причём в первой статье автор жалуется на отсутствие SvnAnt, а во второй статье автор замечает проблему, связанную с использованием 'svn info'. Проблема связана с тем, что 'svn info' выдаёт неточные сведения о ревизии рабочей копии.

Далее рассмотрен пример решения этой проблемы достаточно простым способом.
UPD: добавлен скрипт для git.

Проблема


Суть в том, что команда 'svn info' выдаёт last commit revision элементов указанного каталога.
Например:
$ svn info
Revision: 32

При этом:
$ svn info ./src/ru/bsrgin/myproject/MyActivity.java
Revision: 45
$ svn -r 32 -v log
Changed paths:
    D /some-folder
$ svn -r 45 -v log
Changed paths:
    M /src/ru/bsrgin/myproject/MyActivity.java

Авторы обоих упомянутых статей интерпретируют вывод утилиты как обычный property-файл.
Т.е. наличие строк вида Revision: 32 позволяет им интерпретировать данные как параметры в Build And скрипте. Соответственно задача сильно усложняется, если выполнять 'svn -R info' и искать наиболее позднюю ревизию в выходном файле.

Запрос же 'svn -r HEAD info' выдаёт номер ревизии на сервере, а не в рабочей копии, что тоже неверно, т.к. не выполняется главное условие — генерация актуальной версии ПО. Аргументы BASE, COMMITTED и PREV также не дают ответ на вопрос — из файлов какой версии собран проект?

Решение


Я уже готов был отказаться от метода получения версии ПО, описанного в статье, но вовремя вспомнил про ещё одну утилитку 'svnversion'. Формат вывода данных этой утилитой такой:
4123:4168     mixed revision working copy
4168M         modified working copy
4123S         switched working copy
4123P         partial working copy, from a sparse checkout
4123:4168MS   mixed revision, modified, switched working copy

Т.е. если в моей рабочей копии запустить 'svnversion', то появится такой результат:
$ svnversion
32:46

А если ещё и модифицировать какой-нибудь файл, то:
$ svnversion
32:46M

Собственно говоря, утилита выдаёт полезную информацию, которую хотелось бы включить в версию приложения, но не подходит формат вывода данных. Пришлось немного разобраться с синтаксисом Build Ant скриптов…

Ниже даны инструкции как добавить в свой проект версию рабочей копии subversion или git.

Последовательность действий


Создаём файл svn-revision.build.xml в корне проекта. Вставляем в него следующее содержимое:
<project default="svn-revision">
    <target name="svn-revision">
        <!--
        Выполняем `svnversion -n` для того, чтобы получить текущую ревизию рабочей копии.
        Возможные результаты:
        4123:4168     mixed revision working copy
        4168M         modified working copy
        4123S         switched working copy
        4123P         partial working copy, from a sparse checkout
        4123:4168MS   mixed revision, modified, switched working copy
        -->
        <exec executable="svnversion" output="svnversion.output">
            <arg line="-n"/>
        </exec>
        <loadresource property="svnversion.Revision">
            <file file="svnversion.output"/>
        </loadresource>
        <echo>Revision: ${svnversion.Revision}</echo>
        <!--
        Сохраняем номер ревизии в Manifest файл в конец текстового фрагмента параметра VersionName
        -->
        <replaceregexp file="AndroidManifest.xml"
            match='android:versionName="([^".]+\.[^".]+)(\.[^"]*)?"'
            replace='android:versionName="\1.${svnversion.Revision}"'
        />
        <!--
        Удяляем временные файлы
        -->
        <delete file="svnversion.output"/>
    </target>
</project>

Подразумевается, что AndroidManifest.xml находится в том же самом каталоге, что и svn-revision.build.xml. Если это не так, то модифицируйте 22 строчку. Также подразумевается, что версия приложения выглядит как 1.2 или 1.2.3. Если нет, то модифицируйте 23 строчку.

Далее создаём файл .externalToolBuilders/AddSvnRevisionToVersion.launch и добавляем в него следующие строчки.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ant.AntBuilderLaunchConfigurationType">
  <booleanAttribute key="org.eclipse.ant.ui.ATTR_TARGETS_UPDATED" value="true"/>
  <booleanAttribute key="org.eclipse.ant.ui.DEFAULT_VM_INSTALL" value="false"/>
  <booleanAttribute key="org.eclipse.debug.core.ATTR_REFRESH_RECURSIVE" value="false"/>
  <stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${project}"/>
  <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
    <listEntry value="${project_loc}/svn-revision.build.xml"/>
  </listAttribute>
  <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
    <listEntry value="1"/>
  </listAttribute>
  <booleanAttribute key="org.eclipse.debug.core.capture_output" value="false"/>
  <booleanAttribute key="org.eclipse.debug.ui.ATTR_CONSOLE_OUTPUT_ON" value="false"/>
  <booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
  <stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>
  <booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="true"/>
  <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="ANDROID-APP"/>
  <stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${project_loc}/svn-revision.build.xml"/>
  <stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,"/>
  <booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
</launchConfiguration>

Вместо ANDROID-APP вставляем название своего проекта (смотрим значение тэга <name> в файле .project). Далее модифицируем файл .project — после тэга <buildCommand> добавляем приведённый ниже скрипт:
<buildCommand>
    <name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
    <triggers>full,incremental,</triggers>
    <arguments>
        <dictionary>
            <key>LaunchConfigHandle</key>
            <value><project>/.externalToolBuilders/AddSvnRevisionToVersion.launch</value>
        </dictionary>
    </arguments>
</buildCommand>

В результате первым task-ом будет добавлен процесс, который модифицирует AndroidManifest.xml файл. Файл будет обновляться всякий раз, когда запускается build, также процесс будет запускаться всякий раз, когда собирается release пакет. Если сборка release пакета автоматизирована или вы не пользуетесь Eclipse, то надо настроить ваш сборщик на выполнение svn-revision.build.xml.

Git


Для получения аналогичного результата при использовании SCV git, заменяем файл svn-revision.build.xml на приведённый ниже git-revision.build.xml.
<project default="git-revision">
    <target name="git-revision">
        <!--
        Выполняем 'git describe' для того, чтобы
        получить текущую ревизию рабочей копии.
        См. http://habrahabr.ru/blogs/android_development/132017/#comment_4385225
        -->
        <exec executable="git" output="gitdescribe.output">
            <arg line="describe --always --dirty=+ --abbrev=5"/>
        </exec>
        <loadresource property="git.Revision">
            <file file="gitdescribe.output"/>
        </loadresource>
        <echo>Revision: ${git.Revision}</echo>
        <!--
        Сохраняем номер ревизии в Manifest файл в конец текстового фрагмента
        параметра VersionName
        -->
        <replaceregexp file="AndroidManifest.xml"
            match='android:versionName="([^".]+\.[^".]+)(\.[^"]*)?"'
            replace='android:versionName="\1.${git.Revision}"'
        />
        <!--
        Удяляем временные файлы
        -->
        <delete file="gitdescribe.output"/>
    </target>
</project>

Соответственно не забываем поправить файлы AddSvnRevisionToVersion.launch и .project.

Получение versionName программным способом


Теперь для того, чтобы получить версию ПО, можно воспользоваться таким методом:
public static getApplicationVersion()
{
    try {
        return getInstance().getApplicationContext().getPackageManager()
            .getPackageInfo(getPackageName(), 0).versionName;
    }
    catch (NameNotFoundException e) {
        return "App not installed!";
    }
}

В результате в своём проекте я получил строчку 1.0.32:46M (subversion) и 1.0.58c57+ (git).
Tags:
Hubs:
+14
Comments 13
Comments Comments 13

Articles