DevGang
Авторизоваться

Легко разверните свое приложение Flutter в магазине Google Play

CI/CD, что означает «непрерывная интеграция/непрерывная доставка», предполагает автоматизацию процесса выпуска приложения. Это упрощает традиционно выполняемые вручную и подверженные ошибкам шаги, делая их более эффективными.

Непрерывная интеграция (CI) автоматически выполняет такие задачи, как создание приложений, тестирование и объединение изменений кода с основной базой кода. Непрерывная доставка (CD) управляет развертыванием этих изменений кода в производственной среде. Непрерывное развертывание (CD) делает еще один шаг вперед, автоматически выпуская приложения конечным пользователям.

Внедряя CI/CD, мы стремимся повысить эффективность процесса выпуска приложений, сохраняя при этом его качество и целостность.

В этом исследовании я выбрал GitHub Actions из-за его широкого использования и обширной поддержки. Я опишу шаги, необходимые для интеграции GitHub Actions с сервисами Google Play для Android.

Хотя первоначальная настройка может показаться немного сложной, она значительно упростит процесс доставки приложений нашим клиентам.

Оглавление

  1. Подписание APK
  2. Настройка рабочего процесса
  3. Управление версиями
  4. APK Сборка
  5. Подготовка к выпуску
  6. Развертывание Google Play
  7. План реализации
  8. Заключение

Подписание APK

Предполагая, что у вас есть готовое к выпуску приложение, хранящееся на GitHub, первым шагом в процессе выпуска является подписание APK. Это предполагает создание хранилища ключей для процесса загрузки, которое служит доказательством подлинности вашего приложения.

Для пользователей Windows выполните следующую команду:

keytool -genkey -v -keystore <path>\upload-keystore.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias upload

В назначенном <path> вы сохраните файл хранилища ключей. Если оставить этот раздел пустым, хранилище ключей будет храниться в корневой папке вашего проекта. При появлении запроса укажите и запомните storePassword и keyPassword для последующих шагов. Выполнение приведенной выше команды создаст новый файл с именем upload-keystore.jks в корневом каталоге вашего проекта. Переместите этот файл в папку android > app.

Затем в каталоге Android создайте новый файл с именем key.properties. Обязательно добавьте этот файл в свой .gitignore, чтобы Git не отслеживал его. Заполните файл следующим образом:

storePassword=#{STORE_PASSWORD}#
keyPassword=#{KEY_PASSWORD}#
keyAlias=upload
storeFile=./upload-keystore.jks

После этого перейдите в свой репозиторий GitHub и откройте Settings > Secrets and variables > Actions. Нажмите кнопку New repository secrets и создайте два секрета: один с именем STORE_PASSWORD, а другой — KEY_PASSWORD. Заполните их паролями, которые вы установили при создании файла upload-keystore.jks.

Чтобы Gradle мог использовать хранилище ключей для процесса подписи APK, откройте файл android/app/build.gradle и вставьте следующий код над блоком кода Android. Этот дополнительный код инструктирует Gradle прочитать файл key.properties, который вы создали ранее.

def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
 keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
...
}

Разумеется, укажите конкретный код, который вы хотите добавить, перед блоком кода buildTypes в файле android/app/build.gradle.

signingConfigs {
 release {
 keyAlias keystoreProperties['keyAlias']
 keyPassword keystoreProperties['keyPassword']
 storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
 storePassword keystoreProperties['storePassword']
 }
}

buildTypes {
...
}

После этого обновите содержимое блока кода buildTypes, используя следующий код. Эта модификация гарантирует, что наше приложение будет подписано во время сборки выпуска.

buildTypes {
 release {
  signingConfig signingConfigs.release
 }
}

Чтобы нумерация версий выполнялась автоматически, вам также необходимо изменить содержимое файла pubspec.yaml следующим образом.

name: app_name
description: App description
publish_to: "none"
version: 99.99.99+99

Обратите внимание, что мы изменили переменную версии на 99.99.99+99. Мы установили его в качестве заполнителя, что позволяет удобно изменять номер версии в процессе развертывания.

Подготовка рабочего процесса

Создайте файл YAML с выбранным вами именем в каталоге .github/workflows/. Затем заполните файл следующим образом:

name: Flutter Workflow
on:
  push:
    branches: [main]

Управление версиями

Начальный шаг этого рабочего процесса включает в себя создание номеров версий на основе номеров тегов git. Чтобы получить доступ к информации репозитория, необходимо включить в секреты токен GitHub. Вы можете создать токен GitHub, перейдя по этой ссылке. Имейте в виду, что секретные имена не могут начинаться с термина GITHUB. Например, вы можете сохранить свой токен GitHub как TOKEN_GITHUB.

После того как вы добавили токен GitHub в секреты, мы можем приступить к процессу генерации номера версии. Добавьте следующую строку ниже существующей.

jobs:
 version:
    name: Version Number
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Retrieve Tags and Branch History
        run: |
          git config remote.origin.url @github.com/${{github.repository">https://x-access-token:${{secrets.TOKEN_GITHUB}}@github.com/${{github.repository}}
          git fetch --prune --depth=10000
      - name: Install GitVersion
        uses: gittools/actions/gitversion/setup@v0.9.7
        with:
          versionSpec: "5.x"
      - name: Use GitVersion
        id: gitversion
        uses: gittools/actions/gitversion/execute@v0.9.7
      - name: Creating version.txt with nuGetVersion
        run: echo ${{steps.gitversion.outputs.nuGetVersion}} > version.txt
      - name: Upload version.txt
        uses: actions/upload-artifact@v2
        with:
          name: gitversion
          path: version.txt

Результатом этого этапа будет файл version.txt, в котором будет храниться количество тегов, которое будет служить номером версии нашего приложения на последующих шагах.

В дальнейшем мы настроим действие для создания APK для загрузки в Google Play Store. Прежде чем приступить к этапу сборки APK, обязательно сохраните файл upload-keystore.jks и, если применимо, .env и key.properties в репозитории секретов.

Сначала мы зашифруем файл с помощью gpg и надежно сохраним его содержимое в хранилище секретов. Кроме того, мы создадим секреты для безопасного хранения паролей, связанных с каждым файлом, используемым во время шифрования.

Вы можете зашифровать файл с помощью следующей команды:

gpg -c --armor file_name

Команда создаст файл с расширением .asc. Откройте этот файл, скопируйте его содержимое, а затем вставьте в хранилище секретов. Повторите этот процесс и для других файлов. Ниже приведен готовый пример для справки.

После завершения процесса шифрования и хранения добавьте следующий блок сборки под блоком версии.

build:
  name: Build APK and Creating Release
  needs: [version]
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v1
    - run: |
        echo "${{secrets.RELEASE_KEYSTORE}}" > upload-keystore.jks.asc
        echo "${{secrets.RELEASE_ENV}}" > .env.asc
        echo "${{secrets.RELEASE_PROP}}" > key.properties.asc
        gpg -d --passphrase "${{secrets.RELEASE_KEYSTORE_PASSWORD}}" --batch upload-keystore.jks.asc > android/app/upload-keystore.jks
        gpg -d --passphrase "${{secrets.RELEASE_ENV_PASSWORD}}" --batch .env.asc > .env
        gpg -d --passphrase "${{secrets.RELEASE_PROP_PASSWORD}}" --batch key.properties.asc > android/key.properties
    - name: Get version.txt
      uses: actions/download-artifact@v2
      with:
        name: gitversion
    - name: Create New File Without Newline Char from version.txt
      run: tr -d '\n' < version.txt > version1.txt
    - name: Read Version
      id: version
      uses: juliangruber/read-file-action@v1
      with:
        path: version1.txt
    - name: Update Version in YAML
      run: sed -i 's/99.99.99+99/${{steps.version.outputs.content}}+${{github.run_number}}/g' pubspec.yaml
    - name: Update Keystore Password in Gradle Properties
      run: sed -i 's/#{STORE_PASSWORD}#/${{secrets.STORE_PASSWORD}}/g' android/key.properties
    - name: Update Keystore Key Password in Gradle Properties
      run: sed -i 's/#{KEY_PASSWORD}#/${{secrets.KEY_PASSWORD}}/g' android/key.properties
    - uses: actions/setup-java@v1
      with:
        java-version: "12.x"
    - uses: subosito/flutter-action@v1
      with:
        channel: "stable"
    - run: flutter clean
    - run: flutter pub get
    - run: flutter build apk --release --split-per-abi --obfuscate --split-debug-info=symbols
    - run: flutter build appbundle --release --obfuscate --split-debug-info=symbols
    - name: Create a Release in GitHub
      uses: ncipollo/release-action@v1
      with:
        artifacts: "build/app/outputs/apk/release/*.apk,build/app/outputs/bundle/release/app-release.aab"
        token: ${{secrets.TOKEN_GITHUB}}
        tag: ${{steps.version.outputs.content}}
        commit: ${{github.sha}}
    - name: Upload App Bundle
      uses: actions/upload-artifact@v2
      with:
        name: appbundle
        path: build/app/outputs/bundle/release/app-release.aab

Как показано, мы повторяем секреты для создания файла .asc с соответствующим именем. Затем мы расшифровываем файл .asc, возвращая его в исходное состояние. Это позволяет нам безопасно использовать файлы без необходимости включать их в репозиторий Git.

В этом блоке build мы также заменяем заполнитель номера версии на тот, который мы создали на предыдущем шаге. Кроме того, мы заменяем заполнители для STORE_PASSWORD и KEY_PASSWORD учетными данными, хранящимися в хранилище секретов.

Кроме того, мы выполняем параметр --obfuscate для сборки APK и AAB с повышенными мерами безопасности. Однако важно отметить, что на данном этапе мы не можем включить файл символов в запутанный APK или AAB, поскольку мы не предприняли никаких конкретных шагов для его сохранения.

Подготовка к выпуску

Поздравляем с достижением этого рубежа! На предыдущем этапе мы успешно установили нумерацию версий и провели сборку APK с обфускацией. Кроме того, мы надежно сохранили файлы конфигурации и хранилище ключей в хранилище секретов. После завершения этой подготовки мы готовы установить соединение между Google Cloud Platform и разработчиком Google Play, гарантируя плавный процесс выпуска.

Чтобы начать подготовку, создайте проект и учетную запись службы в Google Cloud. Вы можете создать учетную запись службы через меню IAM & Admin > Service Accounts. После создания учетной записи службы нажмите кнопку выбора справа и откройте меню «Управление ключами». Создайте новый ключ и используйте формат JSON. Скопируйте содержимое файла JSON и сохраните его в хранилище секретов, например, с именем PLAYSTORE_ACCOUNT_KEY, как в примере ниже.

Далее установите соединение между проектом в Google Cloud и соответствующим проектом в консоли Google Play. Убедитесь, что вы включили следующие API как в Google Cloud Platform, так и в консоли Google Play:

  1. API разработчика Google Play для Android
  2. API отчетов для разработчиков Google Play
  3. API публикации игровых сервисов Google Play

Для дополнительной безопасности перейдите в меню User and Permissions в консоли Google Play. Затем выберите адрес электронной почты, связанный с созданной вами учетной записью службы. В разделе App permissions выберите конкретный проект приложения, над которым вы работаете. Этот шаг помогает обеспечить правильный доступ и разрешения для учетной записи службы.

Кроме того, в этом меню у вас есть возможность настроить разрешения учетной записи. Ниже приведен пример разрешений, которые я обычно использую.

Версия Google Play

Примечания. Прежде чем запускать весь этот рабочий процесс, обязательно вручную загрузите файл APK/AAB в консоль Google Play.

На этом заключительном этапе рабочего процесса вы загрузите файл AAB в Play Store через выбранный вами трек. На данный момент доступны два трека: производственный и внутренний. Чтобы продолжить, добавьте блок release ниже существующих блоков.

release:
  name: Release app to production track
  needs: [build]
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v1
    - name: Get appbundle from artifacts
      uses: actions/download-artifact@v2
      with:
        name: appbundle
    - name: Release app to production track
      uses: r0adkll/upload-google-play@v1
      with:
        serviceAccountJsonPlainText: ${{secrets.PLAYSTORE_ACCOUNT_KEY}}
        packageName: com.app.package_name
        releaseFiles: app-release.aab
        track: production
        status: completed

Это этапы CI/CD Play Store с использованием GitHub Actions. Ниже приводится содержимое написанного нами YAML-скрипта в целом.

name: Flutter Stream

on:
  push:
    branches: [main]

jobs:
  version:
    name: Create Version Number
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Feth Histories for All Tags and Branches
        run: |
          git config remote.origin.url @github.com/${{github.repository">https://x-access-token:${{secrets.TOKEN_GITHUB}}@github.com/${{github.repository}}
          git fetch --prune --depth=10000
      - name: Install GitVersion
        uses: gittools/actions/gitversion/setup@v0.9.7
        with:
          versionSpec: "5.x"
      - name: Use GitVersion
        id: gitversion
        uses: gittools/actions/gitversion/execute@v0.9.7
      - name: Create version.txt with nuGetVersion
        run: echo ${{steps.gitversion.outputs.nuGetVersion}} > version.txt
      - name: Upload version.txt
        uses: actions/upload-artifact@v2
        with:
          name: gitversion
          path: version.txt
  build:
    name: Build APK and Create Release
    needs: [version]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - run: |
          echo "${{secrets.RELEASE_KEYSTORE}}" > upload-keystore.jks.asc
          echo "${{secrets.RELEASE_ENV}}" > .env.asc
          echo "${{secrets.RELEASE_PROP}}" > key.properties.asc
          gpg -d --passphrase "${{secrets.RELEASE_KEYSTORE_PASSWORD}}" --batch upload-keystore.jks.asc > android/app/upload-keystore.jks
          gpg -d --passphrase "${{secrets.RELEASE_ENV_PASSWORD}}" --batch .env.asc > .env
          gpg -d --passphrase "${{secrets.RELEASE_PROP_PASSWORD}}" --batch key.properties.asc > android/key.properties
      - name: Get version.txt
        uses: actions/download-artifact@v2
        with:
          name: gitversion
      - name: Create New File Without Newline Char from version.txt
        run: tr -d '\n' < version.txt > version1.txt
      - name: Read Version
        id: version
        uses: juliangruber/read-file-action@v1
        with:
          path: version1.txt
      - name: Update Version in YAML
        run: sed -i 's/99.99.99+99/${{steps.version.outputs.content}}+${{github.run_number}}/g' pubspec.yaml
      - name: Update Keystore Password in Gradle Properties
        run: sed -i 's/#{STORE_PASSWORD}#/${{secrets.STORE_PASSWORD}}/g' android/key.properties
      - name: Update Keystore Key Password in Gradle Properties
        run: sed -i 's/#{KEY_PASSWORD}#/${{secrets.KEY_PASSWORD}}/g' android/key.properties
      - uses: actions/setup-java@v1
        with:
          java-version: "12.x"
      - uses: subosito/flutter-action@v1
        with:
          channel: "stable"
      - run: flutter clean
      - run: flutter pub get
      - run: flutter build apk --release --split-per-abi --obfuscate --split-debug-info=symbols
      - run: flutter build appbundle --release --obfuscate --split-debug-info=symbols
      - name: Create a Release in GitHub
        uses: ncipollo/release-action@v1
        with:
          artifacts: "build/app/outputs/apk/release/*.apk,build/app/outputs/bundle/release/app-release.aab"
          token: ${{secrets.TOKEN_GITHUB}}
          tag: ${{steps.version.outputs.content}}
          commit: ${{github.sha}}
      - name: Upload App Bundle
        uses: actions/upload-artifact@v2
        with:
          name: appbundle
          path: build/app/outputs/bundle/release/app-release.aab
  release:
    name: Release App to Production Track
    needs: [build]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Get Appbundle from Artifacts
        uses: actions/download-artifact@v2
        with:
          name: appbundle
      - name: Release App to Production Track
        uses: r0adkll/upload-google-play@v1
        with:
          serviceAccountJsonPlainText: ${{secrets.PLAYSTORE_ACCOUNT_KEY}}
          packageName: com.package.name
          releaseFiles: app-release.aab
          track: production
          status: completed

Заключение

Внедрив описанный выше конвейер CI/CD, компании могут значительно сократить количество ручных усилий, необходимых для развертывания приложений. Кроме того, использование внутреннего трека Google Play обеспечивает структурированный подход к распространению APK, облегчая такие этапы тестирования, как системное интеграционное тестирование (SIT) или пользовательское приемочное тестирование (UAT). Этот оптимизированный процесс в конечном итоге приводит к более эффективным и надежным выпускам приложений.

Источник:

#Flutter #Android #GitHub
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу