- Aggiunto lo step "Rendi eseguibile gradlew" prima dei build APK e AAB - Allineati capacitor-android.yml e la variante GitHub Fixes #6 @1h30m
262 lines
10 KiB
YAML
262 lines
10 KiB
YAML
name: Capacitor Android
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
node-version:
|
|
type: string
|
|
default: '24.16.0'
|
|
java-version:
|
|
type: string
|
|
default: '21'
|
|
runner:
|
|
type: string
|
|
default: 'ubuntu-latest'
|
|
build-type:
|
|
description: 'debug | release'
|
|
type: string
|
|
default: 'debug'
|
|
output-format:
|
|
description: 'apk (sideload/test) | aab (Play Store)'
|
|
type: string
|
|
default: 'apk'
|
|
version-code:
|
|
description: 'versionCode Android esplicito. Se vuoto viene derivato da package.json: major*10000 + minor*100 + patch'
|
|
type: string
|
|
default: ''
|
|
secrets:
|
|
NPM_TOKEN:
|
|
required: false
|
|
KEYSTORE_BASE64:
|
|
required: false
|
|
KEYSTORE_PASSWORD:
|
|
required: false
|
|
KEY_ALIAS:
|
|
required: false
|
|
KEY_PASSWORD:
|
|
required: false
|
|
GOOGLE_SERVICES_JSON:
|
|
required: false
|
|
|
|
jobs:
|
|
android-build:
|
|
runs-on: ${{ inputs.runner }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: https://gitea.com/actions/checkout@v6
|
|
|
|
- name: Setup Node.js
|
|
uses: https://gitea.com/actions/setup-node@v4
|
|
with:
|
|
node-version: ${{ inputs.node-version || '24.16.0' }}
|
|
|
|
- name: Setup Java
|
|
uses: https://github.com/actions/setup-java@v4
|
|
with:
|
|
distribution: temurin
|
|
java-version: ${{ inputs.java-version || '17' }}
|
|
|
|
- name: Setup Android SDK
|
|
uses: https://github.com/android-actions/setup-android@v4
|
|
|
|
- name: Cache npm
|
|
uses: https://github.com/actions/cache@v4
|
|
with:
|
|
path: ~/.npm
|
|
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-node-
|
|
|
|
- name: Cache Gradle
|
|
uses: https://github.com/actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.gradle/caches
|
|
~/.gradle/wrapper
|
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-gradle-
|
|
|
|
- name: Configure npm private registry
|
|
run: |
|
|
echo "@pzeta:registry=https://gitea.pzetatouch.it/api/packages/PZeta_Touch/npm/" >> ~/.npmrc
|
|
echo "//gitea.pzetatouch.it/api/packages/PZeta_Touch/npm/:_authToken=${{ secrets.NPM_TOKEN }}" >> ~/.npmrc
|
|
|
|
- name: Read app info from package.json
|
|
id: app-info
|
|
run: |
|
|
echo "name=$(jq -r '.name' package.json)" >> $GITHUB_OUTPUT
|
|
echo "version=$(jq -r '.version' package.json)" >> $GITHUB_OUTPUT
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Build web app
|
|
run: npm run build
|
|
|
|
- name: Add Android platform
|
|
run: npx cap add android || true
|
|
|
|
- name: Sync Capacitor
|
|
run: npx cap sync android
|
|
|
|
# La piattaforma Android puo essere generata da 'cap add' (default Capacitor:
|
|
# versionName "1.0", versionCode 1) oppure versionata nel repo consumer.
|
|
# In entrambi i casi la versione pubblicata deve derivare da package.json,
|
|
# unica fonte di verita, altrimenti l'APK/AAB dichiara una versione diversa
|
|
# da quella del tag e Play Store rifiuta upload con versionCode non crescente.
|
|
- name: Inject Android version
|
|
id: android-version
|
|
env:
|
|
PKG_VERSION: ${{ steps.app-info.outputs.version }}
|
|
VERSION_CODE_OVERRIDE: ${{ inputs.version-code }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
if [ -f android/app/build.gradle ]; then
|
|
GRADLE_FILE="android/app/build.gradle"
|
|
KTS=0
|
|
elif [ -f android/app/build.gradle.kts ]; then
|
|
GRADLE_FILE="android/app/build.gradle.kts"
|
|
KTS=1
|
|
else
|
|
echo "::error::Nessun build.gradle in android/app/ — piattaforma Android non generata"
|
|
exit 1
|
|
fi
|
|
|
|
VERSION_NAME="$PKG_VERSION"
|
|
|
|
if [ -n "$VERSION_CODE_OVERRIDE" ]; then
|
|
VERSION_CODE="$VERSION_CODE_OVERRIDE"
|
|
else
|
|
CORE="${PKG_VERSION%%-*}" # scarta il pre-release: 1.2.3-beta.1 -> 1.2.3
|
|
CORE="${CORE%%+*}" # scarta il build metadata: 1.2.3+abc -> 1.2.3
|
|
MAJOR="$(echo "$CORE" | cut -d. -f1)"
|
|
MINOR="$(echo "$CORE" | cut -d. -f2)"
|
|
PATCH="$(echo "$CORE" | cut -d. -f3)"
|
|
MINOR="${MINOR:-0}"
|
|
PATCH="${PATCH:-0}"
|
|
|
|
case "${MAJOR}${MINOR}${PATCH}" in
|
|
''|*[!0-9]*)
|
|
echo "::error::Versione package.json non semver: '$PKG_VERSION'. Passare version-code esplicito."
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
if [ "$MINOR" -gt 99 ] || [ "$PATCH" -gt 99 ]; then
|
|
echo "::error::minor/patch > 99 non rappresentabili ('$PKG_VERSION'). Passare version-code esplicito."
|
|
exit 1
|
|
fi
|
|
|
|
VERSION_CODE=$(( MAJOR * 10000 + MINOR * 100 + PATCH ))
|
|
fi
|
|
|
|
if [ "$KTS" -eq 1 ]; then
|
|
sed -i -E "s/versionCode[[:space:]]*=[[:space:]]*[0-9]+/versionCode = ${VERSION_CODE}/" "$GRADLE_FILE"
|
|
sed -i -E "s/versionName[[:space:]]*=[[:space:]]*\"[^\"]*\"/versionName = \"${VERSION_NAME}\"/" "$GRADLE_FILE"
|
|
else
|
|
sed -i -E "s/versionCode[[:space:]]+[0-9]+/versionCode ${VERSION_CODE}/" "$GRADLE_FILE"
|
|
sed -i -E "s/versionName[[:space:]]+\"[^\"]*\"/versionName \"${VERSION_NAME}\"/" "$GRADLE_FILE"
|
|
fi
|
|
|
|
# Il sed silenzioso e' peggio di un fallimento: senza questa verifica una
|
|
# variazione del template Capacitor produrrebbe build con versione sbagliata.
|
|
# Si confrontano i valori estratti, non si rilegge il file con una regex
|
|
# costruita sulla versione: '+' e '.' di un semver sono metacaratteri ERE.
|
|
# '|| true': senza, pipefail farebbe uscire lo step su un file privo di
|
|
# versionCode/versionName, perdendo la diagnostica del blocco sottostante.
|
|
ACTUAL_CODE="$(grep -oE 'versionCode[[:space:]]*=?[[:space:]]*[0-9]+' "$GRADLE_FILE" | head -1 | grep -oE '[0-9]+$' || true)"
|
|
ACTUAL_NAME="$(grep -oE 'versionName[[:space:]]*=?[[:space:]]*"[^"]*"' "$GRADLE_FILE" | head -1 | cut -d'"' -f2 || true)"
|
|
|
|
if [ "$ACTUAL_CODE" != "$VERSION_CODE" ] || [ "$ACTUAL_NAME" != "$VERSION_NAME" ]; then
|
|
echo "::error::Iniezione versione fallita in $GRADLE_FILE"
|
|
echo " atteso: versionName='${VERSION_NAME}' versionCode=${VERSION_CODE}"
|
|
echo " trovato: versionName='${ACTUAL_NAME}' versionCode=${ACTUAL_CODE}"
|
|
grep -nE 'versionCode|versionName' "$GRADLE_FILE" || true
|
|
exit 1
|
|
fi
|
|
|
|
echo "version-name=${VERSION_NAME}" >> $GITHUB_OUTPUT
|
|
echo "version-code=${VERSION_CODE}" >> $GITHUB_OUTPUT
|
|
echo "Android: versionName=${VERSION_NAME} versionCode=${VERSION_CODE} ($GRADLE_FILE)"
|
|
|
|
- name: Setup google-services.json
|
|
if: secrets.GOOGLE_SERVICES_JSON != ''
|
|
run: echo '${{ secrets.GOOGLE_SERVICES_JSON }}' > android/app/google-services.json
|
|
|
|
- name: Setup release keystore
|
|
if: inputs.build-type == 'release'
|
|
run: echo '${{ secrets.KEYSTORE_BASE64 }}' | base64 -d > android/app/keystore.jks
|
|
|
|
# Il wrapper arriva dal repo senza bit di esecuzione quando il progetto e'
|
|
# committato da Windows: git registra mode 100644 e il checkout sul runner
|
|
# Linux produce "./gradlew: Permission denied". Il chmod qui vale per ogni
|
|
# repo Capacitor, cosi' il difetto non va rincorso uno per uno.
|
|
- name: Rendi eseguibile gradlew
|
|
run: chmod +x android/gradlew
|
|
|
|
- name: Build Android (APK)
|
|
if: inputs.output-format == 'apk'
|
|
working-directory: android
|
|
env:
|
|
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
|
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
|
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
|
run: |
|
|
if [ "${{ inputs.build-type }}" = "release" ]; then
|
|
./gradlew assembleRelease
|
|
else
|
|
./gradlew assembleDebug
|
|
fi
|
|
|
|
- name: Rename APK
|
|
if: inputs.output-format == 'apk'
|
|
run: |
|
|
APP="${{ steps.app-info.outputs.name }}-${{ steps.app-info.outputs.version }}-${{ inputs.build-type }}"
|
|
APK_DIR="android/app/build/outputs/apk/${{ inputs.build-type }}"
|
|
mv "$APK_DIR"/app-${{ inputs.build-type }}.apk "$APK_DIR/${APP}.apk" 2>/dev/null || \
|
|
mv "$APK_DIR"/app-${{ inputs.build-type }}-unsigned.apk "$APK_DIR/${APP}.apk" 2>/dev/null || \
|
|
find "$APK_DIR" -name "*.apk" ! -name "${APP}.apk" -exec mv {} "$APK_DIR/${APP}.apk" \;
|
|
|
|
- name: Build Android (AAB)
|
|
if: inputs.output-format == 'aab'
|
|
working-directory: android
|
|
env:
|
|
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
|
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
|
|
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
|
run: ./gradlew bundleRelease
|
|
|
|
- name: Rename AAB
|
|
if: inputs.output-format == 'aab'
|
|
run: |
|
|
APP="${{ steps.app-info.outputs.name }}-${{ steps.app-info.outputs.version }}-release"
|
|
AAB_DIR="android/app/build/outputs/bundle/release"
|
|
find "$AAB_DIR" -name "*.aab" ! -name "${APP}.aab" -exec mv {} "$AAB_DIR/${APP}.aab" \;
|
|
|
|
- name: Upload APK artifact
|
|
if: inputs.output-format == 'apk'
|
|
uses: https://gitea.com/actions/upload-artifact@v3
|
|
with:
|
|
name: ${{ steps.app-info.outputs.name }}-${{ steps.app-info.outputs.version }}-${{ inputs.build-type }}-apk
|
|
path: android/app/build/outputs/apk/${{ inputs.build-type }}/${{ steps.app-info.outputs.name }}-${{ steps.app-info.outputs.version }}-${{ inputs.build-type }}.apk
|
|
retention-days: 14
|
|
|
|
- name: Upload AAB artifact
|
|
if: inputs.output-format == 'aab'
|
|
uses: https://gitea.com/actions/upload-artifact@v3
|
|
with:
|
|
name: ${{ steps.app-info.outputs.name }}-${{ steps.app-info.outputs.version }}-release-aab
|
|
path: android/app/build/outputs/bundle/release/${{ steps.app-info.outputs.name }}-${{ steps.app-info.outputs.version }}-release.aab
|
|
retention-days: 14
|
|
|
|
- name: Upload Gradle build reports
|
|
if: always()
|
|
uses: https://gitea.com/actions/upload-artifact@v3
|
|
with:
|
|
name: gradle-build-reports
|
|
path: android/build/reports/
|
|
retention-days: 7
|
|
if-no-files-found: ignore
|