mirror of
https://github.com/bitwarden/android.git
synced 2026-11-09 10:34:18 -06:00
🍒 [PM-46265] fix: Install AGP's default NDK in CI so native libraries are stripped (#7568)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
name: 'Setup Android Build'
|
||||
description: 'Setup Android build environment with Gradle, Ruby, and Fastlane'
|
||||
description: 'Setup Android build environment with Gradle, Ruby, JDK, Android NDK, and Fastlane'
|
||||
inputs:
|
||||
java-version:
|
||||
description: 'Java version to use'
|
||||
@@ -22,6 +22,85 @@ runs:
|
||||
distribution: "temurin"
|
||||
java-version: ${{ inputs.java-version }}
|
||||
|
||||
- name: Determine Android NDK versions
|
||||
id: ndk
|
||||
shell: bash
|
||||
run: |
|
||||
# AGP strips native libraries with the NDK reported by printNdkVersion (AGP's default
|
||||
# unless a module sets ndkVersion). Install it so stripping doesn't depend on the runner image.
|
||||
NDK_VERSIONS=$(./gradlew -q printNdkVersion | sed -n 's/^ndkVersion=//p' | sort -u)
|
||||
if [ -z "$NDK_VERSIONS" ]; then
|
||||
echo "::error::Unable to determine the NDK version from ./gradlew printNdkVersion"
|
||||
exit 1
|
||||
fi
|
||||
MISSING_NDK_VERSIONS=""
|
||||
for NDK_VERSION in $NDK_VERSIONS; do
|
||||
if [ -f "$ANDROID_HOME/ndk/$NDK_VERSION/source.properties" ]; then
|
||||
echo "NDK $NDK_VERSION is already installed"
|
||||
else
|
||||
MISSING_NDK_VERSIONS="$MISSING_NDK_VERSIONS $NDK_VERSION"
|
||||
fi
|
||||
done
|
||||
if [ -z "$MISSING_NDK_VERSIONS" ]; then
|
||||
echo "No missing NDK versions found"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Flag installs so it's visible when the runner image no longer ships the NDK AGP expects.
|
||||
echo "::warning title=Android NDK not on runner image::Installing NDK$MISSING_NDK_VERSIONS"
|
||||
{
|
||||
echo "### :warning: Android NDK not on runner image"
|
||||
echo "The following NDK versions were not preinstalled on the runner and were installed by \`setup-android-build\`:"
|
||||
for NDK_VERSION in $MISSING_NDK_VERSIONS; do
|
||||
echo "- \`$NDK_VERSION\`"
|
||||
done
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
{
|
||||
echo "missing=${MISSING_NDK_VERSIONS# }"
|
||||
echo "cache-key=android-ndk-${RUNNER_OS}-$(echo $MISSING_NDK_VERSIONS | tr ' ' '-')"
|
||||
echo "paths<<EOF"
|
||||
for NDK_VERSION in $MISSING_NDK_VERSIONS; do
|
||||
echo "$ANDROID_HOME/ndk/$NDK_VERSION"
|
||||
done
|
||||
echo "EOF"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Restore Android NDK from cache
|
||||
if: ${{ steps.ndk.outputs.missing != '' }}
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ${{ steps.ndk.outputs.paths }}
|
||||
key: ${{ steps.ndk.outputs.cache-key }}
|
||||
|
||||
- name: Install Android NDK
|
||||
if: ${{ steps.ndk.outputs.missing != '' }}
|
||||
shell: bash
|
||||
env:
|
||||
NDK_VERSIONS: ${{ steps.ndk.outputs.missing }}
|
||||
run: |
|
||||
for NDK_VERSION in $NDK_VERSIONS; do
|
||||
NDK_DIR="$ANDROID_HOME/ndk/$NDK_VERSION"
|
||||
if [ -f "$NDK_DIR/source.properties" ]; then
|
||||
echo "NDK $NDK_VERSION restored from cache"
|
||||
continue
|
||||
fi
|
||||
# Downloads occasionally fail or stall with a corrupt archive, so retry with a timeout.
|
||||
for ATTEMPT in 1 2 3; do
|
||||
echo "Installing NDK $NDK_VERSION (attempt $ATTEMPT of 3)"
|
||||
if timeout 10m "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --install "ndk;$NDK_VERSION" > /dev/null \
|
||||
&& [ -f "$NDK_DIR/source.properties" ]; then
|
||||
break
|
||||
fi
|
||||
rm -rf "$NDK_DIR"
|
||||
if [ "$ATTEMPT" -eq 3 ]; then
|
||||
echo "::error::Failed to install NDK $NDK_VERSION after 3 attempts"
|
||||
exit 1
|
||||
fi
|
||||
sleep 15
|
||||
done
|
||||
done
|
||||
|
||||
- name: Install Fastlane
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Fails if any native library packaged for a supported ABI still has its debug symbols.
|
||||
# AGP's strip*DebugSymbols tasks only log a warning when they cannot strip (for example when the
|
||||
# NDK they need is missing), so this check keeps unstripped libraries from shipping unnoticed.
|
||||
# Legacy ABIs (armeabi, mips, mips64) are ignored because current NDKs cannot strip them.
|
||||
# Usage: verify-native-libs-stripped.sh <apk|aab|directory>...
|
||||
|
||||
if [ "$#" -eq 0 ]; then
|
||||
echo "Usage: $0 <apk|aab|directory>..." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SUPPORTED_ABI_LIB_REGEX='(^|/)lib/(arm64-v8a|armeabi-v7a|x86|x86_64)/[^/]+\.so$'
|
||||
|
||||
archives=()
|
||||
for path in "$@"; do
|
||||
if [ -d "$path" ]; then
|
||||
while IFS= read -r archive; do
|
||||
archives+=("$archive")
|
||||
done < <(find "$path" -type f \( -name '*.apk' -o -name '*.aab' \) | sort)
|
||||
elif [ -f "$path" ]; then
|
||||
archives+=("$path")
|
||||
else
|
||||
echo "::error::$path does not exist"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${#archives[@]}" -eq 0 ]; then
|
||||
echo "::error::No APK or AAB files found in: $*"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
work_dir=$(mktemp -d)
|
||||
trap 'rm -rf "$work_dir"' EXIT
|
||||
|
||||
failures=0
|
||||
for archive in "${archives[@]}"; do
|
||||
libs=$(unzip -Z1 "$archive" | grep -E "$SUPPORTED_ABI_LIB_REGEX" || true)
|
||||
if [ -z "$libs" ]; then
|
||||
echo "$archive: no native libraries"
|
||||
continue
|
||||
fi
|
||||
|
||||
extract_dir="$work_dir/$(basename "$archive")"
|
||||
mkdir -p "$extract_dir"
|
||||
count=0
|
||||
while IFS= read -r lib; do
|
||||
unzip -q -o "$archive" "$lib" -d "$extract_dir"
|
||||
count=$((count + 1))
|
||||
if file -b "$extract_dir/$lib" | grep -q "not stripped"; then
|
||||
echo "::error::$archive contains an unstripped native library: $lib"
|
||||
failures=$((failures + 1))
|
||||
fi
|
||||
done <<< "$libs"
|
||||
echo "$archive: checked $count native libraries"
|
||||
done
|
||||
|
||||
if [ "$failures" -gt 0 ]; then
|
||||
echo "::error::Found $failures unstripped native libraries. Check the strip*DebugSymbols output (run with --info) and make sure the NDK reported by ./gradlew printNdkVersion is installed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "All native libraries for supported ABIs are stripped."
|
||||
@@ -174,6 +174,9 @@ jobs:
|
||||
keyAlias:"bitwardenauthenticator" \
|
||||
keyPassword:"$KEY_PASSWORD"
|
||||
|
||||
- name: Verify native libraries are stripped
|
||||
run: ./.github/scripts/verify-native-libs-stripped.sh authenticator/build/outputs
|
||||
|
||||
- name: Upload to GitHub Artifacts - prod.aab
|
||||
if: ${{ matrix.variant == 'aab' }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
|
||||
@@ -122,6 +122,9 @@ jobs:
|
||||
keyAlias:"bitwardentestharness" \
|
||||
keyPassword:"$KEY_PASSWORD"
|
||||
|
||||
- name: Verify native libraries are stripped
|
||||
run: ./.github/scripts/verify-native-libs-stripped.sh testharness/build/outputs
|
||||
|
||||
- name: Upload Test Harness Release APK
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
|
||||
@@ -190,6 +190,10 @@ jobs:
|
||||
run: |
|
||||
bundle exec fastlane assembleDebugApks
|
||||
|
||||
- name: Verify native libraries are stripped
|
||||
if: ${{ (matrix.variant == 'prod') || (matrix.artifact == 'apk') }}
|
||||
run: ./.github/scripts/verify-native-libs-stripped.sh app/build/outputs
|
||||
|
||||
- name: Upload to GitHub Artifacts - prod.aab
|
||||
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'aab') }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
@@ -444,6 +448,9 @@ jobs:
|
||||
keyAlias:bitwarden-beta \
|
||||
keyPassword:$FDROID_BETA_KEY_PASSWORD
|
||||
|
||||
- name: Verify native libraries are stripped
|
||||
run: ./.github/scripts/verify-native-libs-stripped.sh app/build/outputs
|
||||
|
||||
- name: Upload to GitHub Artifacts - fdroid.apk
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
import io.gitlab.arturbosch.detekt.Detekt
|
||||
|
||||
plugins {
|
||||
@@ -180,6 +181,16 @@ fun Project.getGitStagedFiles(rootDir: File): Provider<List<File>> {
|
||||
}
|
||||
|
||||
subprojects {
|
||||
// Prints the NDK version AGP uses to strip native libraries (its default unless a module sets
|
||||
// ndkVersion) so CI can install it without hardcoding a version.
|
||||
plugins.withId("com.android.application") {
|
||||
val android = extensions.getByType<ApplicationExtension>()
|
||||
tasks.register("printNdkVersion") {
|
||||
description = "Prints the NDK version AGP uses to strip native libraries."
|
||||
val ndkVersion = android.ndkVersion
|
||||
doLast { println("ndkVersion=$ndkVersion") }
|
||||
}
|
||||
}
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
options.isFork = true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user