From 9748435d8d9dbbb5cbafc5108d60bb7196fb8141 Mon Sep 17 00:00:00 2001 From: Claudio Nicora Date: Mon, 25 Nov 2024 13:12:21 +0100 Subject: [PATCH] First working CI build workflow --- .github/workflows/build-android.yaml | 83 ++++++++++++++++++++++++++++ android/app/build.gradle | 52 +++++++++++++---- android/gradlew | 0 package.json | 3 +- scripts/android-build.js | 48 ++++++++++++++++ scripts/check-android-version.js | 2 +- 6 files changed, 176 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/build-android.yaml mode change 100644 => 100755 android/gradlew create mode 100644 scripts/android-build.js diff --git a/.github/workflows/build-android.yaml b/.github/workflows/build-android.yaml new file mode 100644 index 0000000..8063ec9 --- /dev/null +++ b/.github/workflows/build-android.yaml @@ -0,0 +1,83 @@ +name: Ionic Android Build and Release + +env: + ANDROID_KEYSTORE_STOREFILE: /tmp/keystore.jks + ANDROID_KEYSTORE_KEYALIAS: ${{ secrets.ANDROID_KEYSTORE_KEYALIAS }} + ANDROID_KEYSTORE_KEYPASSWORD: ${{ secrets.ANDROID_KEYSTORE_KEYPASSWORD }} + ANDROID_KEYSTORE_STOREPASSWORD: ${{ secrets.ANDROID_KEYSTORE_STOREPASSWORD }} + +on: + push: + branches: + - ci + +jobs: + build: + runs-on: ubuntu-latest + + steps: + # Checkout the repository + - name: Checkout Repository + uses: actions/checkout@v3 + + # Extract keystore + - name: Extract keystore content + run: echo "${{ secrets.ANDROID_KEYSTORE_BASE64CONTENT }}" | base64 --decode > $ANDROID_KEYSTORE_STOREFILE + + # Set up Node.js + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 20 + + # Install dependencies + - name: Install Dependencies + run: | + npm install + npm install -g @ionic/cli + + # Set up Java 17 + - name: Set up Java 17 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' # Eclipse Temurin distribution (formerly AdoptOpenJDK) + java-version: '17' + + # Set up Android SDK + - name: Set up JDK and Android SDK + uses: android-actions/setup-android@v2 + + # Build the Android app + - name: Build Android app + run: npm run build:android + + # Upload APK as an artifact + - name: Upload APK + uses: actions/upload-artifact@v4 + with: + name: app-release + path: android/app/build/outputs/apk/release/*.apk + if-no-files-found: error + + ## Extract the latest release notes from CHANGELOG.md + #- name: Extract Release Notes + # id: changelog + # run: | + # # Extract the latest version block from CHANGELOG.md + # latest_release=$(awk '/^## / { if (p) exit; p=1 } p' CHANGELOG.md) + # echo "release_notes<> $GITHUB_ENV + # echo "$latest_release" >> $GITHUB_ENV + # echo "EOF" >> $GITHUB_ENV + + ## Create a GitHub Release using the extracted release notes + #- name: Create GitHub Release + # uses: softprops/action-gh-release@v1 + # with: + # tag_name: v1.0.${{ github.run_number }} # Adjust versioning as needed + # name: "Release v1.0.${{ github.run_number }}" + # body: ${{ env.release_notes }} # Use the extracted release notes + # draft: false + # prerelease: false + # files: platforms/android/app/build/outputs/apk/debug/app-debug.apk # Path to the APK or AAB + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Provided automatically by GitHub diff --git a/android/app/build.gradle b/android/app/build.gradle index 19638c9..1fc3a25 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -12,13 +12,45 @@ apply plugin: 'com.android.application' * keyPassword= * ----------------------------------------------------------------------------------------- */ -System.print("Loading Android keystore config from: ") -System.println(System.getenv("ANDROID_KEYSTORE_PROPS")) -def keystoreProperties = new Properties() -def keystorePropertiesFile = file(System.getenv("ANDROID_KEYSTORE_PROPS")) -if (keystorePropertiesFile.exists()) { - keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) +def _keyAlias = "" +def _keyPassword = "" +def _storeFile = "" +def _storePassword = "" + +// try to get keystore data from a keystore props file +def keystorePropsFilename = System.getenv("ANDROID_KEYSTORE_PROPS") +if (keystorePropsFilename) { + System.out.println("Loading Android keystore config from file: " + keystorePropsFilename) + def keystorePropertiesFile = file(keystorePropsFilename) + if (keystorePropertiesFile.exists()) { + def keystoreProperties = new Properties() + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) + _keyAlias = keystoreProperties['keyAlias'] + _keyPassword = keystoreProperties['keyPassword'] + _storeFile = keystoreProperties['storeFile'] + _storePassword = keystoreProperties['storePassword'] + } else { + System.err.println("File not found!"); + } } +// fallback to env variables (used in CI builds) +else { + System.out.println("Loading Android keystore config from environment variables") + _keyAlias = System.getenv("ANDROID_KEYSTORE_KEYALIAS") + _keyPassword = System.getenv("ANDROID_KEYSTORE_KEYPASSWORD") + _storeFile = System.getenv("ANDROID_KEYSTORE_STOREFILE") + _storePassword = System.getenv("ANDROID_KEYSTORE_STOREPASSWORD") +} + +// load keystore file +if (!keystorePropsFilename && (!_keyAlias || !_keyPassword || !_storeFile || !_storePassword)) { + throw new GradleException("Missing required ANDROID_KEYSTORE_* environment variables or ANDROID_KEYSTORE_PROPS filename"); +} +def keystoreFileContent = file(_storeFile) +if (!keystoreFileContent.exists()) { + throw new GradleException("Specified Android keystore file cannot be found: " + keystoreProperties['storeFile']); +} + android { compileSdkVersion rootProject.ext.compileSdkVersion @@ -42,10 +74,10 @@ android { // signature config (release only) signingConfigs { release { - keyAlias keystoreProperties['keyAlias'] - keyPassword keystoreProperties['keyPassword'] - storeFile file(keystoreProperties['storeFile']) - storePassword keystoreProperties['storePassword'] + keyAlias _keyAlias + keyPassword _keyPassword + storeFile keystoreFileContent + storePassword _storePassword } } buildTypes { diff --git a/android/gradlew b/android/gradlew old mode 100644 new mode 100755 diff --git a/package.json b/package.json index 9251f2c..549a26e 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "fix-android-permissions": "adb shell su -c chmod a+rw /proc/net/unix", "check-i18n-files": "node -r ts-node/register scripts/check-i18n-files.ts", "prebuild:android": "node scripts/check-android-version.js", - "build:android": "ionic capacitor build android --no-open --prod && cd android && gradlew assembleRelease && cd ..", + "build:android": "ionic capacitor build android --no-open --prod", + "postbuild:android": "node scripts/android-build.js", "watch": "ng build --watch --configuration development", "test": "ng test", "lint": "ng lint", diff --git a/scripts/android-build.js b/scripts/android-build.js new file mode 100644 index 0000000..5185926 --- /dev/null +++ b/scripts/android-build.js @@ -0,0 +1,48 @@ +/** + * Run npm build + */ +const process = require('process'); +const { spawn } = require('child_process'); +const isWindows = process.platform === 'win32'; + +// Android commandline +let androidCmdLine = (isWindows ? 'gradlew.bat' : './gradlew'); + +// main +(async () => { + + // Android build + process.chdir('./android'); + try { + await runCommand(androidCmdLine, [ 'assembleRelease' ]); + } catch (error) { + console.error('Error:', error.message); + process.exit(1); + } + finally { + process.chdir('..'); + } + +})(); + + +// Function to run a command and wait for completion +function runCommand(command, args) { + return new Promise((resolve, reject) => { + + const childProcess = spawn(command, args, { shell: isWindows }); + + // Output stdout in real-time + childProcess.stdout.on('data', (data) => console.log(data.toString('utf8'))); + + // Output stderr in real-time + childProcess.stderr.on('data', (data) => console.error(data.toString('utf8'))); + + // Handle process close event + childProcess.on('close', (code) => resolve(code)); + + // Handle errors + childProcess.on('error', (error) => reject(error)); + + }); +} \ No newline at end of file diff --git a/scripts/check-android-version.js b/scripts/check-android-version.js index 8e6df17..f1f2c0e 100644 --- a/scripts/check-android-version.js +++ b/scripts/check-android-version.js @@ -1,5 +1,5 @@ /** - * Create a bunch of test files + * Check if Android version is correctly set */ const fs = require('fs'); const process = require('process');