Skip to content

Merge pull request #17 from HereLiesAz/fix/missing-import-properties-… #17

Merge pull request #17 from HereLiesAz/fix/missing-import-properties-…

Merge pull request #17 from HereLiesAz/fix/missing-import-properties-… #17

Workflow file for this run

name: Merged Build & Release
on:
push:
branches: [ "**" ]
pull_request:
branches: [ "main" ]
workflow_dispatch:
# ⚡ Concurrency: Cancels the entire job if a new push comes in.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-and-release:
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
pull-requests: write
checks: read
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Inject Google Services
env:
GOOGLE_SERVICES_API_KEY: ${{ secrets.GOOGLE_SERVICES_API_KEY }}
PROJECT_ID: ${{ secrets.PROJECT_ID }}
CLIENT_ID: ${{ secrets.CLIENT_ID }}
ARCORE_API_KEY: ${{ secrets.ARCORE_API_KEY }}
run: |
if [ -f "app/google-services.json.template" ]; then
echo "Template found. Injecting secrets..."
sed -e 's/{{\([^}]*\)}}/${\1}/g' app/google-services.json.template | envsubst > app/google-services.json
else
echo "Warning: google-services.json.template not found. Skipping injection."
fi
# Inject ARCore API Key into local.properties for build.gradle.kts to pick up
echo "ARCORE_API_KEY=$ARCORE_API_KEY" >> local.properties
- name: Generate Keystore from Secrets
env:
KEYSTORE_PRIVATE: ${{ secrets.KEYSTORE_PRIVATE }}
KEYSTORE_CHAIN: ${{ secrets.KEYSTORE_CHAIN }}
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
run: |
echo "Generating keystore from certificate chain and private key..."
# Write the private key and certificate chain to temporary files
echo "$KEYSTORE_PRIVATE" > private_key.pem
echo "$KEYSTORE_CHAIN" > certificate_chain.crt
# Create a PKCS12 file from the key and certificate chain
openssl pkcs12 -export -in certificate_chain.crt -inkey private_key.pem \
-name "$KEY_ALIAS" -out keystore.p12 \
-password pass:"$KEYSTORE_PASSWORD"
# Convert the PKCS12 file to a JKS keystore
keytool -importkeystore -srckeystore keystore.p12 -srcstoretype PKCS12 \
-destkeystore app/keystore.jks -deststoretype JKS \
-srcstorepass "$KEYSTORE_PASSWORD" \
-deststorepass "$KEYSTORE_PASSWORD" \
-alias "$KEY_ALIAS" -noprompt
echo "Keystore generated successfully at app/keystore.jks"
# Clean up temporary files
rm private_key.pem certificate_chain.crt keystore.p12
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
with:
cache-read-only: false
dependency-graph: disabled
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
id: gradle-build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
run: |
set +e
export BUILD_NUMBER=$(git rev-list --count HEAD)
# Build the APK
./gradlew assembleDebug -PversionBuild=$BUILD_NUMBER --build-cache \
-Pandroid.injected.signing.store.file=$(pwd)/app/keystore.jks \
-Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD \
-Pandroid.injected.signing.key.alias=$KEY_ALIAS \
-Pandroid.injected.signing.key.password=$KEY_PASSWORD > build.log 2>&1
EXIT_CODE=$?
echo "exit_code=$EXIT_CODE" >> $GITHUB_OUTPUT
cat build.log
exit $EXIT_CODE
# ------------------------------------------------------------------
# FAILURE HANDLING
# ------------------------------------------------------------------
- name: Report Failure to Jules
if: failure() && steps.gradle-build.outcome == 'failure'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
let logTail = 'Log file not found.';
try { logTail = fs.readFileSync('build.log', 'utf8').slice(-4000); } catch (e) {}
const assignee = 'gemini-code-assist';
try {
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `Build Failure on ${context.eventName === 'pull_request' ? 'PR #' + context.issue.number : context.ref}`,
body: `@${assignee} /jules debug this build error\n\n**Log:**\n\`\`\`\n${logTail}\n\`\`\``,
labels: ['jules', 'bug'],
assignees: [assignee]
});
} catch (error) {
console.log("Failed to create issue with assignee. Retrying without assignee.");
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `Build Failure on ${context.eventName === 'pull_request' ? 'PR #' + context.issue.number : context.ref}`,
body: `@${assignee} /jules debug this build error\n\n**Log:**\n\`\`\`\n${logTail}\n\`\`\``,
labels: ['jules', 'bug']
});
}
# ------------------------------------------------------------------
# RELEASE STEPS (Only run on PUSH, not Pull Requests)
# ------------------------------------------------------------------
- name: Prepare Release Variables
if: success() && github.event_name == 'push'
id: release_vars
run: |
export BUILD_NUMBER=$(git rev-list --count HEAD)
# Extract version info from properties file
MAJOR=$(grep 'versionMajor=' version.properties | cut -d'=' -f2 | tr -d '\r ')
MINOR=$(grep 'versionMinor=' version.properties | cut -d'=' -f2 | tr -d '\r ')
# Calculate Patch version programmatically based on commits since Minor update
MINOR_COMMIT=$(git blame -L '/versionMinor=/',+1 version.properties | awk '{print $1}' | tr -d '^')
if [ -n "$MINOR_COMMIT" ]; then
PATCH=$(git rev-list --count $MINOR_COMMIT..HEAD)
else
PATCH=$(grep 'versionPatch=' version.properties | cut -d'=' -f2 | tr -d '\r ')
fi
VERSION_NAME="$MAJOR.$MINOR.$PATCH.$BUILD_NUMBER"
TAG_NAME="latest-debug-v${MAJOR}.${MINOR}"
# Dynamic App Name from settings.gradle
if [ -f "settings.gradle.kts" ]; then
APP_NAME=$(grep 'rootProject.name' settings.gradle.kts | sed -E "s/.*=.*[\"'](.*)[\"']/\1/")
elif [ -f "settings.gradle" ]; then
APP_NAME=$(grep 'rootProject.name' settings.gradle | sed -E "s/.*=.*[\"'](.*)[\"']/\1/")
else
APP_NAME="App"
fi
# Locate built APK
APK_ORIGINAL=$(find app/build/outputs/apk/debug -name "*.apk" | head -n 1)
if [ -z "$APK_ORIGINAL" ]; then
echo "Error: No APK found to release!"
exit 1
fi
TARGET_NAME="${APP_NAME}-${VERSION_NAME}-debug.apk"
# Rename for release
mv "$APK_ORIGINAL" "$TARGET_NAME"
echo "TAG_NAME=$TAG_NAME" >> $GITHUB_ENV
echo "APK_FILE=$TARGET_NAME" >> $GITHUB_ENV
# Package Build Tools (Optional)
mkdir -p build_tools_package/tools build_tools_package/native
[ -d "app/src/main/assets/tools" ] && cp -r app/src/main/assets/tools/* build_tools_package/tools/ || true
[ -d "app/src/main/jniLibs/arm64-v8a" ] && cp -r app/src/main/jniLibs/arm64-v8a/* build_tools_package/native/ || true
cd build_tools_package && zip -r ../build-tools.zip . && cd ..
- name: Publish Release
if: success() && github.event_name == 'push'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Force update tag to point to current commit
git tag -fa $TAG_NAME -m "Latest Debug Build"
git push origin $TAG_NAME --force
BUILD_TOOLS="build-tools.zip"
# Check if release exists
if gh release view $TAG_NAME > /dev/null 2>&1; then
echo "Updating existing release..."
# Clean up ONLY the build-tools asset to avoid conflicts
gh release delete-asset "$TAG_NAME" "$BUILD_TOOLS" -y || true
gh release edit $TAG_NAME --prerelease \
--title "Latest Debug Build ($TAG_NAME)" \
--notes "Auto-build from commit $GITHUB_SHA" \
--target $GITHUB_SHA
# Clobber ensures that if we re-run the workflow on the exact same commit, it replaces the APK instead of failing.
gh release upload $TAG_NAME "$APK_FILE" --clobber
[ -f "$BUILD_TOOLS" ] && gh release upload $TAG_NAME "$BUILD_TOOLS" --clobber
else
echo "Creating new release..."
gh release create $TAG_NAME "$APK_FILE" --prerelease \
--title "Latest Debug Build ($TAG_NAME)" \
--notes "Auto-build from commit $GITHUB_SHA" \
--target $GITHUB_SHA
[ -f "$BUILD_TOOLS" ] && gh release upload $TAG_NAME "$BUILD_TOOLS" --clobber
fi