From 3da4da341eebebaff6103cc9272090abdbdd51ea Mon Sep 17 00:00:00 2001 From: sozinov Date: Fri, 24 Jul 2026 10:37:28 +0300 Subject: [PATCH 1/3] MOBILE-0000: ci/cd for example --- .github/bump_example_versions.sh | 60 +++++++ .github/workflows/publish-example-to-play.yml | 152 ++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100755 .github/bump_example_versions.sh create mode 100644 .github/workflows/publish-example-to-play.yml diff --git a/.github/bump_example_versions.sh b/.github/bump_example_versions.sh new file mode 100755 index 00000000..640c0c45 --- /dev/null +++ b/.github/bump_example_versions.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Bumps the example app version for a Google Play release: +# - versionCode: current value in example/app/build.gradle + 1 +# - versionName: synced with SDK_VERSION_NAME from gradle.properties +# Creates branch example-release/-vc, commits and pushes it. +# Writes version_code / version_name / release_branch to GITHUB_OUTPUT when available. + +build_gradle="example/app/build.gradle" +properties_file="gradle.properties" + +current_code=$(grep -E '^[[:space:]]*versionCode[[:space:]]+[0-9]+' "$build_gradle" | grep -oE '[0-9]+' | head -n 1) +if [ -z "$current_code" ]; then + echo "Failed to read versionCode from $build_gradle" + exit 1 +fi + +version_name=$(grep -E '^SDK_VERSION_NAME=' "$properties_file" | cut -d'=' -f2) +if [ -z "$version_name" ]; then + echo "Failed to read SDK_VERSION_NAME from $properties_file" + exit 1 +fi + +new_code=$((current_code + 1)) +release_branch="example-release/${version_name}-vc${new_code}" + +if [ -n "$(git ls-remote --heads origin "refs/heads/$release_branch")" ]; then + echo "Branch $release_branch already exists on origin." + echo "A previous release run probably failed after pushing. Merge or delete its branch/PR and re-run." + exit 1 +fi + +echo "Bump example versionCode from $current_code to $new_code, versionName to $version_name." + +git checkout -b "$release_branch" + +sed -i -E "s/^([[:space:]]*)versionCode[[:space:]]+[0-9]+/\1versionCode $new_code/" "$build_gradle" +sed -i -E "s/^([[:space:]]*)versionName[[:space:]]+\"[^\"]*\"/\1versionName \"$version_name\"/" "$build_gradle" + +git add -f "$build_gradle" +if git diff --cached --quiet; then + echo "Nothing to commit — the version bump produced no changes. Aborting instead of pushing an empty branch." + exit 1 +fi +git commit -m "Bump example app to versionName $version_name, versionCode $new_code" + +echo "Pushing changes to branch: $release_branch" +if ! git push origin "$release_branch"; then + echo "Failed to push changes to the origin $release_branch" + exit 1 +fi + +if [ -n "${GITHUB_OUTPUT:-}" ]; then + { + echo "version_code=$new_code" + echo "version_name=$version_name" + echo "release_branch=$release_branch" + } >> "$GITHUB_OUTPUT" +fi diff --git a/.github/workflows/publish-example-to-play.yml b/.github/workflows/publish-example-to-play.yml new file mode 100644 index 00000000..c81089a3 --- /dev/null +++ b/.github/workflows/publish-example-to-play.yml @@ -0,0 +1,152 @@ +name: Publish Example to Google Play + +on: + workflow_dispatch: + inputs: + branch: + description: 'Branch to release the example app from' + required: true + default: 'develop' + +permissions: + contents: read + +concurrency: + group: publish-example-to-play + cancel-in-progress: false + +jobs: + bump_version: + name: Bump example version + runs-on: ubuntu-latest + permissions: + contents: write + outputs: + version_code: ${{ steps.bump.outputs.version_code }} + version_name: ${{ steps.bump.outputs.version_name }} + release_branch: ${{ steps.bump.outputs.release_branch }} + steps: + - name: Checkout source branch + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.event.inputs.branch }} + fetch-depth: 0 + submodules: recursive + + - name: Configure Git identity for GitHub Action Bot + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Bump versionCode and versionName + id: bump + run: ./.github/bump_example_versions.sh + + create_pr: + name: Create Pull Request + runs-on: ubuntu-latest + needs: bump_version + steps: + - name: Checkout release branch + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ needs.bump_version.outputs.release_branch }} + + - name: Create PR via GitHub CLI + env: + GH_TOKEN: ${{ secrets.PAT_FOR_TRIGGERING_BRANCH_PROTECTION }} + SRC: ${{ needs.bump_version.outputs.release_branch }} + DST: ${{ github.event.inputs.branch }} + VERSION_NAME: ${{ needs.bump_version.outputs.version_name }} + VERSION_CODE: ${{ needs.bump_version.outputs.version_code }} + run: | + gh pr create \ + --base "$DST" \ + --head "$SRC" \ + --title "Example app release $VERSION_NAME (versionCode $VERSION_CODE)" \ + --body "Automated version bump for the example app Google Play release. Merged automatically after a successful upload to Google Play." + + build_and_upload: + name: Build and upload to Google Play + runs-on: ubuntu-latest + needs: bump_version + steps: + - name: Checkout release branch + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ needs.bump_version.outputs.release_branch }} + submodules: recursive + + - name: Set up JDK 17 + uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0 + with: + distribution: 'temurin' + java-version: '17' + cache: 'gradle' + + - name: Setup Android SDK + uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1 + + - name: Restore signing and config files from secrets + env: + UPLOAD_KEYSTORE_BASE64: ${{ secrets.EXAMPLE_UPLOAD_KEYSTORE_BASE64 }} + KEYSTORE_STORE_PASSWORD: ${{ secrets.EXAMPLE_KEYSTORE_STORE_PASSWORD }} + KEYSTORE_KEY_ALIAS: ${{ secrets.EXAMPLE_KEYSTORE_KEY_ALIAS }} + KEYSTORE_KEY_PASSWORD: ${{ secrets.EXAMPLE_KEYSTORE_KEY_PASSWORD }} + GOOGLE_SERVICES_JSON: ${{ secrets.EXAMPLE_GOOGLE_SERVICES_JSON }} + AGCONNECT_SERVICES_JSON: ${{ secrets.EXAMPLE_AGCONNECT_SERVICES_JSON }} + EXAMPLE_PROPERTIES: ${{ secrets.EXAMPLE_PROPERTIES }} + run: | + set -euo pipefail + printf '%s' "$UPLOAD_KEYSTORE_BASE64" | base64 -d > example/upload_key_gp.jks + { + echo "storeFile=upload_key_gp.jks" + echo "storePassword=$KEYSTORE_STORE_PASSWORD" + echo "keyAlias=$KEYSTORE_KEY_ALIAS" + echo "keyPassword=$KEYSTORE_KEY_PASSWORD" + } > example/keystore.properties + printf '%s' "$GOOGLE_SERVICES_JSON" > example/app/google-services.json + printf '%s' "$AGCONNECT_SERVICES_JSON" > example/app/agconnect-services.json + printf '%s' "$EXAMPLE_PROPERTIES" > example/example.properties + + - name: Build release bundle + working-directory: example + run: ./gradlew --no-daemon bundleRelease + + - name: Attach AAB to workflow run + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: example-release-bundle + path: example/app/build/outputs/bundle/release/app-release.aab + + - name: Upload to Google Play + uses: r0adkll/upload-google-play@e738b9dd8f2476ea806d921b64aacd24f34515a5 # v1.1.5 + with: + serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }} + packageName: com.mindbox.example + releaseFiles: example/app/build/outputs/bundle/release/app-release.aab + track: internal + status: draft + releaseName: ${{ needs.bump_version.outputs.version_name }} (${{ needs.bump_version.outputs.version_code }}) + + merge_pr: + name: Merge version bump PR + runs-on: ubuntu-latest + needs: [bump_version, create_pr, build_and_upload] + steps: + - name: Checkout release branch + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ needs.bump_version.outputs.release_branch }} + + - name: Merge PR via GitHub CLI + env: + GH_TOKEN: ${{ secrets.PAT_FOR_TRIGGERING_BRANCH_PROTECTION }} + RELEASE_BRANCH: ${{ needs.bump_version.outputs.release_branch }} + run: | + pr_number=$(gh pr list --head "$RELEASE_BRANCH" --json number --jq '.[0].number // empty') + if [ -z "$pr_number" ]; then + echo "No open PR found for branch $RELEASE_BRANCH" + exit 1 + fi + gh pr merge "$pr_number" --merge --auto --delete-branch From 23ce7381cbe7e4ebbc413b8f2aaff27262d22f96 Mon Sep 17 00:00:00 2001 From: sozinov Date: Fri, 24 Jul 2026 11:34:06 +0300 Subject: [PATCH 2/3] MOBILE-0000: restore keystore into app module dir storeFile in keystore.properties is resolved via file() inside the :app module, so the workflow decodes the keystore straight into example/app/ where Gradle actually looks for it. Co-Authored-By: Claude Fable 5 --- .github/workflows/publish-example-to-play.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-example-to-play.yml b/.github/workflows/publish-example-to-play.yml index c81089a3..5f8e3c45 100644 --- a/.github/workflows/publish-example-to-play.yml +++ b/.github/workflows/publish-example-to-play.yml @@ -98,7 +98,7 @@ jobs: EXAMPLE_PROPERTIES: ${{ secrets.EXAMPLE_PROPERTIES }} run: | set -euo pipefail - printf '%s' "$UPLOAD_KEYSTORE_BASE64" | base64 -d > example/upload_key_gp.jks + printf '%s' "$UPLOAD_KEYSTORE_BASE64" | base64 -d > example/app/upload_key_gp.jks { echo "storeFile=upload_key_gp.jks" echo "storePassword=$KEYSTORE_STORE_PASSWORD" From c30278d1180164ca3de9ca236a7a98f3dbbdad45 Mon Sep 17 00:00:00 2001 From: sozinov Date: Sat, 25 Jul 2026 12:58:10 +0300 Subject: [PATCH 3/3] MOBILE-0000: follow review --- .github/bump_example_versions.sh | 60 ----------- .github/workflows/publish-example-to-play.yml | 100 +++++------------- example/app/build.gradle | 18 +++- example/gradle.properties | 4 +- 4 files changed, 45 insertions(+), 137 deletions(-) delete mode 100755 .github/bump_example_versions.sh diff --git a/.github/bump_example_versions.sh b/.github/bump_example_versions.sh deleted file mode 100755 index 640c0c45..00000000 --- a/.github/bump_example_versions.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Bumps the example app version for a Google Play release: -# - versionCode: current value in example/app/build.gradle + 1 -# - versionName: synced with SDK_VERSION_NAME from gradle.properties -# Creates branch example-release/-vc, commits and pushes it. -# Writes version_code / version_name / release_branch to GITHUB_OUTPUT when available. - -build_gradle="example/app/build.gradle" -properties_file="gradle.properties" - -current_code=$(grep -E '^[[:space:]]*versionCode[[:space:]]+[0-9]+' "$build_gradle" | grep -oE '[0-9]+' | head -n 1) -if [ -z "$current_code" ]; then - echo "Failed to read versionCode from $build_gradle" - exit 1 -fi - -version_name=$(grep -E '^SDK_VERSION_NAME=' "$properties_file" | cut -d'=' -f2) -if [ -z "$version_name" ]; then - echo "Failed to read SDK_VERSION_NAME from $properties_file" - exit 1 -fi - -new_code=$((current_code + 1)) -release_branch="example-release/${version_name}-vc${new_code}" - -if [ -n "$(git ls-remote --heads origin "refs/heads/$release_branch")" ]; then - echo "Branch $release_branch already exists on origin." - echo "A previous release run probably failed after pushing. Merge or delete its branch/PR and re-run." - exit 1 -fi - -echo "Bump example versionCode from $current_code to $new_code, versionName to $version_name." - -git checkout -b "$release_branch" - -sed -i -E "s/^([[:space:]]*)versionCode[[:space:]]+[0-9]+/\1versionCode $new_code/" "$build_gradle" -sed -i -E "s/^([[:space:]]*)versionName[[:space:]]+\"[^\"]*\"/\1versionName \"$version_name\"/" "$build_gradle" - -git add -f "$build_gradle" -if git diff --cached --quiet; then - echo "Nothing to commit — the version bump produced no changes. Aborting instead of pushing an empty branch." - exit 1 -fi -git commit -m "Bump example app to versionName $version_name, versionCode $new_code" - -echo "Pushing changes to branch: $release_branch" -if ! git push origin "$release_branch"; then - echo "Failed to push changes to the origin $release_branch" - exit 1 -fi - -if [ -n "${GITHUB_OUTPUT:-}" ]; then - { - echo "version_code=$new_code" - echo "version_name=$version_name" - echo "release_branch=$release_branch" - } >> "$GITHUB_OUTPUT" -fi diff --git a/.github/workflows/publish-example-to-play.yml b/.github/workflows/publish-example-to-play.yml index 5f8e3c45..d68e3014 100644 --- a/.github/workflows/publish-example-to-play.yml +++ b/.github/workflows/publish-example-to-play.yml @@ -16,66 +16,36 @@ concurrency: cancel-in-progress: false jobs: - bump_version: - name: Bump example version + build_and_upload: + name: Build and upload to Google Play runs-on: ubuntu-latest - permissions: - contents: write - outputs: - version_code: ${{ steps.bump.outputs.version_code }} - version_name: ${{ steps.bump.outputs.version_name }} - release_branch: ${{ steps.bump.outputs.release_branch }} steps: - name: Checkout source branch uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.event.inputs.branch }} - fetch-depth: 0 submodules: recursive - - name: Configure Git identity for GitHub Action Bot - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - - name: Bump versionCode and versionName - id: bump - run: ./.github/bump_example_versions.sh - - create_pr: - name: Create Pull Request - runs-on: ubuntu-latest - needs: bump_version - steps: - - name: Checkout release branch - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ needs.bump_version.outputs.release_branch }} - - - name: Create PR via GitHub CLI + - name: Compute example version + id: version env: - GH_TOKEN: ${{ secrets.PAT_FOR_TRIGGERING_BRANCH_PROTECTION }} - SRC: ${{ needs.bump_version.outputs.release_branch }} - DST: ${{ github.event.inputs.branch }} - VERSION_NAME: ${{ needs.bump_version.outputs.version_name }} - VERSION_CODE: ${{ needs.bump_version.outputs.version_code }} + RUN_NUMBER: ${{ github.run_number }} run: | - gh pr create \ - --base "$DST" \ - --head "$SRC" \ - --title "Example app release $VERSION_NAME (versionCode $VERSION_CODE)" \ - --body "Automated version bump for the example app Google Play release. Merged automatically after a successful upload to Google Play." - - build_and_upload: - name: Build and upload to Google Play - runs-on: ubuntu-latest - needs: bump_version - steps: - - name: Checkout release branch - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ needs.bump_version.outputs.release_branch }} - submodules: recursive + set -euo pipefail + base_code=$(grep -E '^EXAMPLE_VERSION_CODE=' example/gradle.properties | cut -d'=' -f2) + if [ -z "$base_code" ]; then + echo "Failed to read EXAMPLE_VERSION_CODE from example/gradle.properties" + exit 1 + fi + version_code=$((base_code + RUN_NUMBER)) + version_name=$(grep -E '^SDK_VERSION_NAME=' gradle.properties | cut -d'=' -f2) + if [ -z "$version_name" ]; then + echo "Failed to read SDK_VERSION_NAME from gradle.properties" + exit 1 + fi + echo "Building versionCode=$version_code versionName=$version_name" + echo "version_code=$version_code" >> "$GITHUB_OUTPUT" + echo "version_name=$version_name" >> "$GITHUB_OUTPUT" - name: Set up JDK 17 uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0 @@ -111,7 +81,11 @@ jobs: - name: Build release bundle working-directory: example - run: ./gradlew --no-daemon bundleRelease + env: + VERSION_CODE: ${{ steps.version.outputs.version_code }} + run: | + ./gradlew --no-daemon bundleRelease \ + -PEXAMPLE_VERSION_CODE="$VERSION_CODE" - name: Attach AAB to workflow run uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 @@ -127,26 +101,4 @@ jobs: releaseFiles: example/app/build/outputs/bundle/release/app-release.aab track: internal status: draft - releaseName: ${{ needs.bump_version.outputs.version_name }} (${{ needs.bump_version.outputs.version_code }}) - - merge_pr: - name: Merge version bump PR - runs-on: ubuntu-latest - needs: [bump_version, create_pr, build_and_upload] - steps: - - name: Checkout release branch - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - ref: ${{ needs.bump_version.outputs.release_branch }} - - - name: Merge PR via GitHub CLI - env: - GH_TOKEN: ${{ secrets.PAT_FOR_TRIGGERING_BRANCH_PROTECTION }} - RELEASE_BRANCH: ${{ needs.bump_version.outputs.release_branch }} - run: | - pr_number=$(gh pr list --head "$RELEASE_BRANCH" --json number --jq '.[0].number // empty') - if [ -z "$pr_number" ]; then - echo "No open PR found for branch $RELEASE_BRANCH" - exit 1 - fi - gh pr merge "$pr_number" --merge --auto --delete-branch + releaseName: ${{ steps.version.outputs.version_name }} (${{ steps.version.outputs.version_code }}) diff --git a/example/app/build.gradle b/example/app/build.gradle index 327c4080..d598a8ba 100644 --- a/example/app/build.gradle +++ b/example/app/build.gradle @@ -21,6 +21,17 @@ if (examplePropertiesFile.exists()) { } } + +def exampleVersionName = EXAMPLE_VERSION_NAME_FALLBACK +def sdkPropertiesFile = rootProject.file("../gradle.properties") +if (sdkPropertiesFile.exists()) { + def sdkProperties = new Properties() + sdkPropertiesFile.withInputStream { stream -> + sdkProperties.load(stream) + } + exampleVersionName = sdkProperties.getProperty('SDK_VERSION_NAME', exampleVersionName) +} + android { namespace 'com.mindbox.example' compileSdk 36 @@ -29,8 +40,11 @@ android { applicationId "com.mindbox.example" minSdk 21 targetSdk 36 - versionCode 14 - versionName "2.15.2" + versionCode EXAMPLE_VERSION_CODE.toInteger() + // versionName is taken from SDK_VERSION_NAME in the android-sdk repo root + // gradle.properties; if it cannot be read (e.g. the example is built outside + // the repo), it falls back to EXAMPLE_VERSION_NAME_FALLBACK. + versionName exampleVersionName multiDexEnabled true buildConfigField "String", "MINDBOX_DOMAIN", "\"${exampleProperties.getProperty('mindbox.domain', '')}\"" diff --git a/example/gradle.properties b/example/gradle.properties index 937abc2b..f436b2af 100644 --- a/example/gradle.properties +++ b/example/gradle.properties @@ -22,4 +22,6 @@ kotlin.code.style=official # thereby reducing the size of the R class for that library android.nonTransitiveRClass=true #Huawei doesn't work with AGP 8 without flag bellow -apmsInstrumentationEnabled=false \ No newline at end of file +apmsInstrumentationEnabled=false +EXAMPLE_VERSION_CODE=14 +EXAMPLE_VERSION_NAME_FALLBACK=2.15.2 \ No newline at end of file