Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
189 changes: 189 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
name: Android CI/CD - Play Store Release

permissions:
contents: write

on:
push:
branches:
- main
workflow_dispatch:
inputs:
track:
description: 'Play Store track (internal, alpha, beta, production)'
required: false
default: 'internal'
type: choice
options:
- internal
- alpha
- beta
- production

env:
JAVA_VERSION: '17'
JAVA_DISTRIBUTION: 'temurin'

jobs:
build-and-deploy:
if: "!contains(github.event.head_commit.message, '[skip release]')"
runs-on: ubuntu-latest
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions:
contents: write

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: ${{ env.JAVA_DISTRIBUTION }}
java-version: ${{ env.JAVA_VERSION }}

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Bump version
id: version
run: |
CURRENT_VERSION=$(grep -oP 'versionName\s*=\s*"\K[^"]+' app/build.gradle.kts)
CURRENT_VERSION_CODE=$(grep -oP 'versionCode\s*=\s*\K\d+' app/build.gradle.kts)

if [ -z "$CURRENT_VERSION" ]; then
CURRENT_VERSION="1.0.0"
fi
if [ -z "$CURRENT_VERSION_CODE" ]; then
CURRENT_VERSION_CODE=0
fi

IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
if [ -z "$PATCH" ]; then PATCH=0; fi
NEW_PATCH=$((PATCH + 1))
NEW_VERSION="${MAJOR}.${MINOR}.${NEW_PATCH}"

NEW_VERSION_CODE=$((CURRENT_VERSION_CODE + 1))

echo "Updating version from $CURRENT_VERSION ($CURRENT_VERSION_CODE) to $NEW_VERSION ($NEW_VERSION_CODE)"

sed -i "s/versionCode = $CURRENT_VERSION_CODE/versionCode = $NEW_VERSION_CODE/" app/build.gradle.kts
sed -i "s/versionName = \"$CURRENT_VERSION\"/versionName = \"$NEW_VERSION\"/" app/build.gradle.kts

git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add app/build.gradle.kts
git commit -m "chore: bump version to $NEW_VERSION ($NEW_VERSION_CODE) [skip ci]"
git push

echo "VERSION_CODE=$NEW_VERSION_CODE" >> $GITHUB_OUTPUT
echo "VERSION_NAME=$NEW_VERSION" >> $GITHUB_OUTPUT

- name: Decode Keystore
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
run: |
printf '%s' "$KEYSTORE_BASE64" | base64 -d > keystore.jks
echo "Keystore decoded to keystore.jks"

- name: Verify Keystore Integrity
run: |
echo "Keystore file size: $(wc -c < keystore.jks) bytes"
if [ ! -s keystore.jks ]; then
echo "❌ ERROR: Keystore file is empty or missing!"
exit 1
fi
echo "Keystore file exists and is non-empty"

- name: Cache Gradle packages
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-

- name: Create local.properties
run: |
echo "sdk.dir=/usr/local/lib/android/sdk" > local.properties

- name: Build AAB
env:
KEYSTORE_PATH: ${{ github.workspace }}/keystore.jks
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
run: |
./gradlew bundleRelease --stacktrace

- name: Verify AAB exists
run: |
AAB_PATH="app/build/outputs/bundle/release/app-release.aab"
if [ ! -f "$AAB_PATH" ]; then
echo "AAB not found!"
exit 1
fi
echo "AAB found at $AAB_PATH"

- name: Upload AAB as artifact
uses: actions/upload-artifact@v4
with:
name: app-release-${{ steps.version.outputs.VERSION_CODE }}
path: app/build/outputs/bundle/release/app-release.aab
retention-days: 30

- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ steps.version.outputs.VERSION_NAME }}-${{ steps.version.outputs.VERSION_CODE }}
name: Release v${{ steps.version.outputs.VERSION_NAME }} (Code ${{ steps.version.outputs.VERSION_CODE }})
files: app/build/outputs/bundle/release/app-release.aab
generate_release_notes: true

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true

- name: Install Fastlane
run: |
gem install fastlane -NV

- name: Deploy to Play Store
continue-on-error: true
env:
PLAY_JSON_KEY_DATA: ${{ secrets.PLAY_JSON_KEY_DATA }}
run: |
if [ -z "$PLAY_JSON_KEY_DATA" ]; then
echo "Skipping Play Store deployment: PLAY_JSON_KEY_DATA secret is not set."
exit 0
fi

if [ "${{ github.event_name }}" == "push" ]; then
TRACK="production"
else
TRACK="${{ github.event.inputs.track || 'internal' }}"
fi
echo "Deploying to track: $TRACK"

case $TRACK in
internal) fastlane deploy_internal ;;
alpha) fastlane deploy_alpha ;;
beta) fastlane deploy_beta ;;
production) fastlane deploy_production ;;
*) fastlane deploy_internal ;;
esac

- name: Build Summary
run: |
echo "## Release Summary" >> $GITHUB_STEP_SUMMARY
echo "- **Version Code**: ${{ steps.version.outputs.VERSION_CODE }}" >> $GITHUB_STEP_SUMMARY
echo "- **Version Name**: ${{ steps.version.outputs.VERSION_NAME }}" >> $GITHUB_STEP_SUMMARY
echo "- **Status**: ✅ Release built and GitHub Release created" >> $GITHUB_STEP_SUMMARY
159 changes: 159 additions & 0 deletions .github/workflows/test-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
name: Android CI/CD - Build Only

permissions:
contents: write

on:
push:
branches:
- main
- feature/ci-cd
workflow_dispatch:

env:
JAVA_VERSION: '17'
JAVA_DISTRIBUTION: 'temurin'

jobs:
build-test:
if: "!contains(github.event.head_commit.message, '[skip ci]')"
runs-on: ubuntu-latest
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions:
contents: write

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: ${{ env.JAVA_DISTRIBUTION }}
java-version: ${{ env.JAVA_VERSION }}

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Decode Keystore
env:
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
run: |
printf '%s' "$KEYSTORE_BASE64" | base64 -d > keystore.jks
echo "Keystore decoded to keystore.jks"

- name: Verify Keystore Integrity
run: |
echo "Keystore file size: $(wc -c < keystore.jks) bytes"
echo "SHA256: $(sha256sum keystore.jks | cut -d' ' -f1)"
if [ ! -s keystore.jks ]; then
echo "ERROR: Keystore file is empty or missing!"
exit 1
fi
echo "Keystore file exists and is non-empty"

- name: Read current version
id: version
run: |
CURRENT_VERSION=$(grep -oP 'versionName\s*=\s*"\K[^"]+' app/build.gradle.kts)
CURRENT_VERSION_CODE=$(grep -oP 'versionCode\s*=\s*\K\d+' app/build.gradle.kts)

if [ -z "$CURRENT_VERSION" ]; then
CURRENT_VERSION="1.0.0"
echo "Fallback version: $CURRENT_VERSION"
fi

if [ -z "$CURRENT_VERSION_CODE" ]; then
CURRENT_VERSION_CODE=0
echo "Fallback version code: $CURRENT_VERSION_CODE"
fi

echo "VERSION_CODE=$CURRENT_VERSION_CODE" >> $GITHUB_OUTPUT
echo "VERSION_NAME=$CURRENT_VERSION" >> $GITHUB_OUTPUT
echo "Using version code: $CURRENT_VERSION_CODE"
echo "Using version name: $CURRENT_VERSION"

- name: Cache Gradle packages
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-

- name: Create local.properties
run: |
echo "sdk.dir=/usr/local/lib/android/sdk" > local.properties

- name: Verify Keystore Credentials
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
run: |
echo "Testing keystore with provided credentials..."
echo "KEY_ALIAS value: $KEY_ALIAS"
echo "KEYSTORE_PASSWORD length: ${#KEYSTORE_PASSWORD}"
echo "KEY_PASSWORD length: ${#KEY_PASSWORD}"
keytool -list -keystore keystore.jks -storepass "$KEYSTORE_PASSWORD" -v 2>&1 | head -20 || echo "❌ Store password is WRONG"
echo "---"
keytool -keypasswd -keystore keystore.jks -storepass "$KEYSTORE_PASSWORD" -alias "$KEY_ALIAS" -keypass "$KEY_PASSWORD" -new "$KEY_PASSWORD" 2>&1 || echo "❌ Key password or alias is WRONG"

- name: Build AAB
env:
KEYSTORE_PATH: ${{ github.workspace }}/keystore.jks
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
VERSION_CODE: ${{ steps.version.outputs.VERSION_CODE }}
VERSION_NAME: ${{ steps.version.outputs.VERSION_NAME }}
run: |
./gradlew bundleRelease --stacktrace

- name: Verify AAB exists
run: |
AAB_PATH="app/build/outputs/bundle/release/app-release.aab"
if [ ! -f "$AAB_PATH" ]; then
echo "AAB not found!"
find app/build/outputs -name "*.aab" || true
exit 1
fi
echo "AAB found: $AAB_PATH"
ls -lh "$AAB_PATH"

- name: Upload AAB as artifact
uses: actions/upload-artifact@v4
with:
name: app-release-${{ steps.version.outputs.VERSION_CODE }}
path: |
app/build/outputs/bundle/release/app-release.aab
retention-days: 30

- name: Build Summary
run: |
echo "## Build Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Version Code**: ${{ steps.version.outputs.VERSION_CODE }}" >> $GITHUB_STEP_SUMMARY
echo "- **Version Name**: ${{ steps.version.outputs.VERSION_NAME }}" >> $GITHUB_STEP_SUMMARY
echo "- **Status**: ✅ Build successful" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "The AAB artifact has been uploaded and can be downloaded from the workflow run." >> $GITHUB_STEP_SUMMARY

- name: Notify build status
if: always()
run: |
if [ ${{ job.status }} == 'success' ]; then
echo "Build successful"
echo "Version: ${{ steps.version.outputs.VERSION_NAME }}"
echo "Download AAB from workflow artifacts"
else
echo "Build failed"
echo "Check workflow logs for details"
exit 1
fi
29 changes: 0 additions & 29 deletions .idea/appInsightsSettings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading