diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b6c50b5..00906ec 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,15 +8,15 @@ on: pull_request: branches: [ master ] +env: + NDK_VERSION: '25.2.9519653' + NODE_VERSION: '16' + JAVA_VERSION: '17' + jobs: build-rust: - name: Build aw-server-rust (ndk-${{ matrix.ndk_version }}, node-${{ matrix.node_version }}) - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-20.04] - ndk_version: ['25.2.9519653'] - node_version: [16] + name: Build aw-server-rust + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 with: @@ -32,7 +32,8 @@ jobs: cache-name: jniLibs with: path: mobile/src/main/jniLibs/ - key: ${{ env.cache-name }}-release-${{ env.RELEASE }}-ndk-${{ matrix.ndk_version }}-${{ hashFiles('.git/modules/aw-server-rust/HEAD') }} + key: ${{ env.cache-name }}-release-${{ env.RELEASE }}-ndk-${{ env.NDK_VERSION }}-${{ hashFiles('.git/modules/aw-server-rust/HEAD') }} + - name: Display structure of downloaded files if: steps.cache-jniLibs.outputs.cache-hit == 'true' run: | @@ -45,8 +46,8 @@ jobs: - name: Install NDK if: steps.cache-jniLibs.outputs.cache-hit != 'true' run: | - sdkmanager "ndk;${{ matrix.ndk_version }}" - ANDROID_NDK_HOME="$ANDROID_SDK_ROOT/ndk/${{ matrix.ndk_version }}" + sdkmanager "ndk;${{ env.NDK_VERSION }}" + ANDROID_NDK_HOME="$ANDROID_SDK_ROOT/ndk/${{ env.NDK_VERSION }}" ls $ANDROID_NDK_HOME echo "ANDROID_NDK_HOME=$ANDROID_NDK_HOME" >> $GITHUB_ENV @@ -81,35 +82,28 @@ jobs: run: | test -e mobile/src/main/jniLibs/x86_64/libaw_server.so - - name: Upload JNI libs - uses: actions/upload-artifact@v3 - with: - name: jniLibs - path: mobile/src/main/jniLibs/ - build-apk: - name: Build APK (${{ matrix.os }}, java-${{ matrix.java_version }}) - runs-on: ${{ matrix.os }} + name: Build ${{ matrix.type }} + runs-on: ubuntu-20.04 needs: [build-rust] env: SUPPLY_TRACK: production # used by fastlane to determine track to publish to strategy: - fail-fast: false + fail-fast: true matrix: - os: [ubuntu-20.04] - java_version: [17] - ndk_version: ['25.2.9519653'] + type: ['apk', 'aab'] + steps: - uses: actions/checkout@v2 with: submodules: 'recursive' - + - uses: ActivityWatch/check-version-format-action@v2 id: version with: prefix: 'v' - + - name: Echo version run: | echo "${{ steps.version.outputs.full }} (stable: ${{ steps.version.outputs.is_stable }})" @@ -130,15 +124,15 @@ jobs: - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: ${{ matrix.java_version }} + java-version: ${{ env.JAVA_VERSION }} # Android SDK & NDK - name: Set up Android SDK uses: android-actions/setup-android@v2 - name: Install NDK run: | - sdkmanager "ndk;${{ matrix.ndk_version }}" - ANDROID_NDK_HOME="$ANDROID_SDK_ROOT/ndk/${{ matrix.ndk_version }}" + sdkmanager "ndk;${{ env.NDK_VERSION }}" + ANDROID_NDK_HOME="$ANDROID_SDK_ROOT/ndk/${{ env.NDK_VERSION }}" ls $ANDROID_NDK_HOME echo "ANDROID_NDK_HOME=$ANDROID_NDK_HOME" >> $GITHUB_ENV @@ -152,11 +146,17 @@ jobs: - name: Install age uses: adnsio/setup-age-action@v1.2.0 - - name: Download JNI libs - uses: actions/download-artifact@v3 + # Restores jniLibs from cache + # `actions/cache/restore` only restores, without saving back in a post-hook + - uses: actions/cache/restore@v3 + id: cache-jniLibs + env: + cache-name: jniLibs with: - name: jniLibs - path: mobile/src/main/jniLibs + path: mobile/src/main/jniLibs/ + key: ${{ env.cache-name }}-release-${{ env.RELEASE }}-ndk-${{ env.NDK_VERSION }}-${{ hashFiles('.git/modules/aw-server-rust/HEAD') }} + fail-on-cache-miss: true + - name: Check that jniLibs present run: | test -e mobile/src/main/jniLibs/x86_64/libaw_server.so @@ -183,11 +183,6 @@ jobs: - name: Update versionCode run: bundle exec fastlane update_version - # Test - - name: Test - run: | - make test - - name: Load Android secrets if: env.KEY_ANDROID_JKS != null env: @@ -197,24 +192,76 @@ jobs: cat android.jks.age | age -d -i android.jks.key -o android.jks rm android.jks.key - - name: Assemble APK + - name: Assemble env: JKS_STOREPASS: ${{ secrets.KEY_ANDROID_JKS_STOREPASS }} JKS_KEYPASS: ${{ secrets.KEY_ANDROID_JKS_KEYPASS }} run: | - make dist/aw-android.apk + make dist/aw-android.${{ matrix.type }} - - name: Upload release APK + - name: Upload uses: actions/upload-artifact@v3 with: name: aw-android - path: dist/aw-android*.apk + path: dist/aw-android*.${{ matrix.type }} + + test: + name: Test + runs-on: ubuntu-20.04 + needs: [build-rust] + env: + SUPPLY_TRACK: production # used by fastlane to determine track to publish to + + steps: + - uses: actions/checkout@v2 + with: + submodules: 'recursive' + + - name: Set RELEASE + run: | + echo "RELEASE=${{ startsWith(github.ref_name, 'v') }}" >> $GITHUB_ENV + + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: ${{ env.JAVA_VERSION }} + + # Android SDK & NDK + - name: Set up Android SDK + uses: android-actions/setup-android@v2 + - name: Install NDK + run: | + sdkmanager "ndk;${{ env.NDK_VERSION }}" + ANDROID_NDK_HOME="$ANDROID_SDK_ROOT/ndk/${{ env.NDK_VERSION }}" + ls $ANDROID_NDK_HOME + echo "ANDROID_NDK_HOME=$ANDROID_NDK_HOME" >> $GITHUB_ENV + + + # Restores jniLibs from cache + # `actions/cache/restore` only restores, without saving back in a post-hook + - uses: actions/cache/restore@v3 + id: cache-jniLibs + env: + cache-name: jniLibs + with: + path: mobile/src/main/jniLibs/ + key: ${{ env.cache-name }}-release-${{ env.RELEASE }}-ndk-${{ env.NDK_VERSION }}-${{ hashFiles('.git/modules/aw-server-rust/HEAD') }} + fail-on-cache-miss: true + - name: Check that jniLibs present + run: | + test -e mobile/src/main/jniLibs/x86_64/libaw_server.so + + # Test + - name: Test + run: | + make test + test-e2e: - name: Test E2E ${{ matrix.android_avd }} #-${{ matrix.os }}-eAPI-${{ matrix.android_emu_version }}-java-${{ matrix.java_version }}-node-${{ matrix.node_version }} + name: Test E2E needs: [build-rust] if: false # disabled - runs-on: ${{ matrix.os }} + runs-on: "macos-12" # macOS-latest env: MATRIX_E_SDK: ${{ matrix.android_emu_version }} MATRIX_AVD: ${{ matrix.android_avd }} @@ -222,10 +269,7 @@ jobs: fail-fast: false max-parallel: 1 matrix: - os: [macos-12] # macOS-latest, android_avd: [Pixel_API_27_AOSP] - java_version: [11] - ndk_version: ['25.2.9519653'] include: - android_avd: Pixel_API_27_AOSP android_emu_version: 27 @@ -284,7 +328,7 @@ jobs: - name: Start Android emulator timeout-minutes: 30 # ~4min normal - 3x DOSafety env: - SUFFIX: ${{ matrix.android_avd }}-eAPI-${{ matrix.android_emu_version }}-${{ matrix.os }} + SUFFIX: ${{ matrix.android_avd }}-eAPI-${{ matrix.android_emu_version }} HOMEBREW_NO_INSTALL_CLEANUP: 1 run: | echo "Starting emulator and waiting for boot to complete...." @@ -338,15 +382,15 @@ jobs: - name: Set up JDK uses: actions/setup-java@v1 with: - java-version: ${{ matrix.java_version }} + java-version: ${{ env.JAVA_VERSION }} # Android SDK & NDK - name: Set up Android SDK uses: android-actions/setup-android@v2 - name: Install NDK run: | - sdkmanager "ndk;${{ matrix.ndk_version }}" - ANDROID_NDK_HOME="$ANDROID_SDK_ROOT/ndk/${{ matrix.ndk_version }}" + sdkmanager "ndk;${{ env.NDK_VERSION }}" + ANDROID_NDK_HOME="$ANDROID_SDK_ROOT/ndk/${{ env.NDK_VERSION }}" ls $ANDROID_NDK_HOME echo "ANDROID_NDK_HOME=$ANDROID_NDK_HOME" >> $GITHUB_ENV @@ -366,7 +410,7 @@ jobs: - name: Screenshot if: ${{ success() || steps.test.conclusion == 'failure'}} env: - SUFFIX: ${{ matrix.android_avd }}-eAPI-${{ matrix.android_emu_version }}-${{ matrix.os }} + SUFFIX: ${{ matrix.android_avd }}-eAPI-${{ matrix.android_emu_version }} run: | adb shell monkey -p net.activitywatch.android.debug 1 sleep 10 @@ -412,13 +456,13 @@ jobs: # cat GITHUB_STEP_SUMMARY.html >> $GITHUB_STEP_SUMMARY release-fastlane: - needs: [build-apk] + needs: [build-apk, test] if: startsWith(github.ref, 'refs/tags/v') # only on runs triggered from tag runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Download release APK + - name: Download APK & AAB uses: actions/download-artifact@v3 with: name: aw-android @@ -464,16 +508,17 @@ jobs: - name: Release with fastlane run: | - bundle exec fastlane supply run --apk dist/*.apk + # bundle exec fastlane supply run --apk dist/*.apk + bundle exec fastlane supply run --aab dist/*.aab release-gh: - needs: [build-apk] + needs: [build-apk, test] if: startsWith(github.ref, 'refs/tags/v') # only on runs triggered from tag runs-on: ubuntu-latest steps: # Will download all artifacts to path - - name: Download release APK + - name: Download release APK & AAB uses: actions/download-artifact@v3 with: name: aw-android @@ -495,6 +540,8 @@ jobs: with: draft: true prerelease: ${{ !(steps.version.outputs.is_stable == 'true') }} # must compare to true, since boolean outputs are actually just strings, and "false" is truthy since it's not empty: https://github.com/actions/runner/issues/1483#issuecomment-994986996 - files: dist/*.apk + files: | + dist/*.apk + dist/*.aab # body_path: dist/release_notes/release_notes.md diff --git a/.gitignore b/.gitignore index b1861ad..badbaec 100644 --- a/.gitignore +++ b/.gitignore @@ -51,15 +51,7 @@ captures/ # IntelliJ *.iml -.idea/workspace.xml -.idea/tasks.xml -.idea/gradle.xml -.idea/assetWizardSettings.xml -.idea/modules.xml -.idea/dictionaries -.idea/libraries -.idea/caches -.idea/deploymentTargetDropDown.xml +.idea # Keystore files # Uncomment the following lines if you do not want to check your keystore files in. diff --git a/Makefile b/Makefile index f6a972c..f49b0a3 100644 --- a/Makefile +++ b/Makefile @@ -5,9 +5,11 @@ SHELL := /bin/bash # - https://developer.android.com/ndk/guides/android_mk RELEASE_TYPE = $(shell test -n "$$RELEASE" && $$RELEASE && echo 'release' || echo 'debug') +RELEASE_TYPE_CAPS = $(shell test -n "$$RELEASE" && $$RELEASE && echo 'Release' || echo 'Debug') HAS_SECRETS = $(shell test -n "$$JKS_KEYPASS" && echo 'true' || echo 'false') APKDIR = mobile/build/outputs/apk +AABDIR = mobile/build/outputs/bundle WEBUI_SRCDIR := aw-server-rust/aw-webui WEBUI_DISTDIR := $(WEBUI_SRCDIR)/dist @@ -16,6 +18,9 @@ WEBUI_DISTDIR := $(WEBUI_SRCDIR)/dist all: aw-server-rust build: all +# builds an app bundle, puts it in dist +build-bundle: dist/aw-android.aab + # builds a complete, signed apk, puts it in dist build-apk: dist/aw-android.apk @@ -55,32 +60,43 @@ install-apk-debug: $(APKDIR)/debug/mobile-debug.apk adb install $(APKDIR)/debug/mobile-debug-androidTest.apk # APK targets -$(APKDIR)/release/mobile-release-unsigned.apk: - TERM=xterm ./gradlew assembleRelease - tree $(APKDIR) - -$(APKDIR)/debug/mobile-debug.apk: - TERM=xterm ./gradlew assembleDebug +$(APKDIR)/$(RELEASE_TYPE)/mobile-$(RELEASE_TYPE).apk: + TERM=xterm ./gradlew assemble$(RELEASE_TYPE_CAPS) tree $(APKDIR) -$(APKDIR)/androidTest/debug/mobile-debug-androidTest.apk: +$(APKDIR)/androidTest/$(RELEASE_TYPE)/mobile-$(RELEASE_TYPE)-androidTest.apk: TERM=xterm ./gradlew assembleAndroidTest tree $(APKDIR) +# App bundle targets +$(AABDIR)/$(RELEASE_TYPE)/mobile-$(RELEASE_TYPE).aab: + TERM=xterm ./gradlew bundle$(RELEASE_TYPE_CAPS) + tree $(AABDIR) + +# Signed release bundle +dist/aw-android.aab: $(AABDIR)/$(RELEASE_TYPE)/mobile-$(RELEASE_TYPE).aab + mkdir -p dist + @# Only sign if we have key secrets set ($JKS_KEYPASS and $JKS_STOREPASS) +ifneq ($(HAS_SECRETS), true) + @echo "No key secrets set, not signing" + cp $< $@ +else + ./scripts/sign_apk.sh $< $@ +endif + # Signed release APK -dist/aw-android.apk: $(APKDIR)/release/mobile-release-unsigned.apk - @# TODO: Name the APK based on the version number or commit hash. +dist/aw-android.apk: $(APKDIR)/$(RELEASE_TYPE)/mobile-$(RELEASE_TYPE).apk mkdir -p dist @# Only sign if we have key secrets set ($JKS_KEYPASS and $JKS_STOREPASS) ifneq ($(HAS_SECRETS), true) - @echo "No key secrets set, not signing APK" + @echo "No key secrets set, not signing" cp $< $@ else ./scripts/sign_apk.sh $< $@ endif # for mobile-debug.apk and mobile-debug-androidTest.apk -dist/debug/%: $(APKDIR)/debug/% +dist/$(RELEASE_TYPE)/%: $(APKDIR)/$(RELEASE_TYPE)/% mkdir -p dist cp $< $@ @@ -103,7 +119,7 @@ TARGET_x64 := $(TARGET)/x86_64-linux-android TARGET_x86 := $(TARGET)/i686-linux-android # Build webui specifically for Android (disabled update check, different default views, etc) -export ON_ANDROID := -- --android +export ON_ANDROID := -- --android aw-server-rust: $(JNILIBS) @@ -158,8 +174,3 @@ clean: rm -rf mobile/src/main/assets/webui rm -rf mobile/src/main/jniLibs -test: - bundle exec fastlane test - # ./gradlew clean lint test - # ./gradlew connectedAndroidTest # || true - diff --git a/mobile/build.gradle b/mobile/build.gradle index 159a0ec..f4d5243 100644 --- a/mobile/build.gradle +++ b/mobile/build.gradle @@ -29,6 +29,9 @@ android { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + ndk { + debugSymbolLevel 'SYMBOL_TABLE' + } } debug { applicationIdSuffix ".debug" diff --git a/scripts/sign_apk.sh b/scripts/sign_apk.sh index 6c27bbf..4feb860 100755 --- a/scripts/sign_apk.sh +++ b/scripts/sign_apk.sh @@ -1,5 +1,7 @@ #!/bin/bash +# Signs APKs or AABs using the android.jks keystore + set -e input=$1 @@ -22,19 +24,31 @@ if [ -z $JKS_KEYPASS ]; then fi # Zipalign -zipalign=$(find $ANDROID_HOME/build-tools -name "zipalign" -print | head -n 1) -$zipalign -v -p 4 $input $input.new -mv $input.new $input +# Not needed for AABs +if [[ $input == *.apk ]]; then + zipalign=$(find $ANDROID_HOME/build-tools -name "zipalign" -print | head -n 1) + $zipalign -v -p 4 $input $input.new + mv $input.new $input +fi # Sign -# Using apksigner instead of jarsigner since API 30+: https://stackoverflow.com/a/69473649 -apksigner=$(find $ANDROID_HOME/build-tools -name "apksigner" -print | head -n 1) -$apksigner sign --ks android.jks --ks-key-alias activitywatch \ - --ks-pass env:JKS_STOREPASS --key-pass env:JKS_KEYPASS \ - $input - -# Verify -$apksigner verify $input +# Using apksigner for APKs instead of jarsigner since API 30+: https://stackoverflow.com/a/69473649 +# Using jarsigner for AABs since apksigner doesn't support them +if [[ $input == *.apk ]]; then + apksigner=$(find $ANDROID_HOME/build-tools -name "apksigner" -print | head -n 1) + $apksigner sign --ks android.jks --ks-key-alias activitywatch \ + --ks-pass env:JKS_STOREPASS --key-pass env:JKS_KEYPASS \ + $input + + # Verify + $apksigner verify $input +fi +if [[ $input == *.aab ]]; then + jarsigner -verbose \ + -keystore android.jks \ + -storepass $JKS_STOREPASS -keypass $JKS_KEYPASS \ + $input activitywatch +fi # Move to output destination mv $input $output