feat: Add flutter-build.sh
This commit is contained in:
228
src/flutter-build.sh
Normal file
228
src/flutter-build.sh
Normal file
@@ -0,0 +1,228 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Parse CLI arguments
|
||||
# (Thanks StackOverflow https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash)
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--name)
|
||||
# The name of the application. Only useful for displaying in the script.
|
||||
# If not specified, will use the "name: " attribute from the pubspec.yaml
|
||||
NAME=$2
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--not-signed)
|
||||
# Tells the script that the APKs are *NOT* signed and have to be manually
|
||||
# signed.
|
||||
ALREADY_SIGNED=n
|
||||
shift
|
||||
;;
|
||||
--zipalign)
|
||||
# The path to the zipalign binary. If not specified, lets the shell resolve the path
|
||||
# itself.
|
||||
# Only useful with --not-signed.
|
||||
ZIPALIGN=$2
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--apksigner)
|
||||
# The path to the apksigner binary. If not specified, lets the shell resolve the path
|
||||
# itself.
|
||||
# Only useful with --not-signed.
|
||||
APKSIGNER=$2
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--notify-send)
|
||||
# The path to the notify-send binary. If not specified, lets the shell resolve the path
|
||||
# itself.
|
||||
# Only useful with --not-signed.
|
||||
NOTIFY_SEND=$2
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--provider-config)
|
||||
# The path to the provider config passed to apksigner via --provider-arg.
|
||||
# Only useful with --not-signed.
|
||||
PROVIDER_CONFIG=$2
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--no-clean)
|
||||
# Does not run "flutter clean" before running the build.
|
||||
CLEAN_BUILD=n
|
||||
shift
|
||||
;;
|
||||
--min-sdk-version)
|
||||
# The minimum SDK version that should be able to validate the APK signature.
|
||||
# Only useful with --not-signed.
|
||||
MIN_SDK_VERSION=$2
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
--check)
|
||||
# Prints all parsed options and exits.
|
||||
JUST_CHECK=y
|
||||
shift
|
||||
;;
|
||||
--skip-build)
|
||||
# Skips the entire build but not the signing, if --not-signed is specified. If --not-signed
|
||||
# is not specified, then the build is skipped, but the APKs are copied into the correct folder.
|
||||
# This flag expects that the build has run at least once and the correct release directory
|
||||
# already exists.
|
||||
SKIP_BUILD=y
|
||||
shift
|
||||
;;
|
||||
--no-notification)
|
||||
# Does not create a notification when asking for the PIN of the Yubikey.
|
||||
# Only useful with --not-signed.
|
||||
SEND_NOTIFICATION=n
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Extract the name, if not passed
|
||||
if [[ -z "$NAME" ]]; then
|
||||
NAME=$(grep -E "^name: " pubspec.yaml | cut -b 6-)
|
||||
fi
|
||||
|
||||
# Default values
|
||||
APKSIGNER=${APKSIGNER:-apksigner}
|
||||
ZIPALIGN=${ZIPALIGN:-zipalign}
|
||||
ALREADY_SIGNED=${ALREADY_SIGNED:-y}
|
||||
MIN_SDK_VERSION=${MIN_SDK_VERSION:-24}
|
||||
CLEAN_BUILD=${CLEAN_BUILD:-y}
|
||||
SKIP_BUILD=${SKIP_BUILD:-n}
|
||||
NOTIFY_SEND=${NOTIFY_SEND:-notify-send}
|
||||
SEND_NOTIFICATION=${SEND_NOTIFICATION:-y}
|
||||
|
||||
# Parse version info
|
||||
version=$(grep -E "^version: " pubspec.yaml | cut -b 10-)
|
||||
IFS="+" read -ra version_parts <<< "$version"
|
||||
version_code="${version_parts[1]}"
|
||||
release_dir="./release-${version}"
|
||||
|
||||
# Print a header
|
||||
echo "===== ${NAME} ====="
|
||||
echo "Building version ${version}"
|
||||
echo "Moving APKs into ${release_dir} after build"
|
||||
echo "Clean build: ${CLEAN_BUILD}"
|
||||
echo "Skipping build: ${SKIP_BUILD}"
|
||||
echo "Sending notification: ${SEND_NOTIFICATION}"
|
||||
echo "APKs already signed: ${ALREADY_SIGNED}"
|
||||
if [[ "$ALREADY_SIGNED" = "n" ]]; then
|
||||
echo "Used zipalign: ${ZIPALIGN}"
|
||||
echo "Used apksigner: ${APKSIGNER}"
|
||||
echo "Provider config: ${PROVIDER_CONFIG}"
|
||||
echo "Minimum SDK verifiability: ${MIN_SDK_VERSION}"
|
||||
fi
|
||||
|
||||
if [[ "${JUST_CHECK}" = "y" ]]; then
|
||||
echo
|
||||
echo "Terminating here because --check was used"
|
||||
exit
|
||||
fi
|
||||
|
||||
# For better readability, print an empty line
|
||||
echo
|
||||
|
||||
# Check if we have a changelog file for that version
|
||||
if [[ ! -f "./fastlane/metadata/android/en-US/changelogs/$version_code.txt" ]]; then
|
||||
echo "Warning: No changelog item for $version_code"
|
||||
fi
|
||||
|
||||
if [[ "${SKIP_BUILD}" = "y" ]]; then
|
||||
echo "Skipping build because of --skip-build"
|
||||
else
|
||||
# Create the directory
|
||||
[[ -d "${release_dir}" ]] && echo "Warning: Release directory ${release_dir} already exists"
|
||||
mkdir "${release_dir}" || true
|
||||
|
||||
if [[ "${CLEAN_BUILD}" = "y" ]]; then
|
||||
# Clean flutter build
|
||||
flutter clean
|
||||
fi
|
||||
|
||||
# Get dependencies
|
||||
flutter pub get
|
||||
|
||||
# Build everything again
|
||||
flutter pub run build_runner build --delete-conflicting-outputs
|
||||
|
||||
# Build the release apk
|
||||
flutter build apk \
|
||||
--release \
|
||||
--split-per-abi \
|
||||
--split-debug-info="${release_dir}/debug-info"
|
||||
fi
|
||||
|
||||
# If we have to sign, ask for the PIN upfront
|
||||
if [[ "$ALREADY_SIGNED" = "n" ]]; then
|
||||
[[ "${SEND_NOTIFICATION}" = "y" ]] && ${NOTIFY_SEND} \
|
||||
--urgency normal \
|
||||
--app-name "Flutter Build" \
|
||||
--icon dialog-password \
|
||||
"Signing PIN required" \
|
||||
"The build is done and the PIN for manually signing the APKs must be entered"
|
||||
|
||||
# Thanks https://geoffreymetais.github.io/code/key-signing/#scripting
|
||||
echo "Please enter Yubikey PIN code "
|
||||
stty -echo
|
||||
trap 'stty echo' EXIT
|
||||
read -p 'PIN: ' YUBI_PIN
|
||||
stty echo
|
||||
trap - EXIT
|
||||
fi
|
||||
|
||||
# Move everything
|
||||
for platform in arm64-v8a armeabi-v7a x86_64; do
|
||||
echo "Processing the $platform release..."
|
||||
|
||||
raw_apk="build/app/outputs/flutter-apk/app-${platform}-release.apk"
|
||||
if [[ "$ALREADY_SIGNED" = "y" ]]; then
|
||||
# Simply copy artifacts
|
||||
cp -f "$raw_apk" "${release_dir}/${NAME}-${platform}-release.apk"
|
||||
else
|
||||
# https://developer.android.com/build/building-cmdline#sign_cmdline
|
||||
aligned_apk="${release_dir}/app-${platform}-release-aligned.apk"
|
||||
signed_apk="${release_dir}/app-${platform}-release.apk"
|
||||
${ZIPALIGN} -p 4 "$raw_apk" "${aligned_apk}"
|
||||
|
||||
# NOTE: "-J-add-exports jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED" tells the wrapper that is
|
||||
# `apksigner` to append "--add-exports jdk.[...]" as a parameter to the Java CLI. It is in
|
||||
# quotation marks such that it is passed with only one (1) dash (it would otherwise be "--add-exports -jdk.[...]",
|
||||
# which is wrong).
|
||||
# At least on NixOS, apksigner refuses to use the sun.security.pkcs11.SunPKCS11 provider because
|
||||
# it is not exported for its unnamed class. So, we have to do a little trickery to "export" the
|
||||
# class to apksigner.
|
||||
${APKSIGNER} "-J-add-exports jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED" sign \
|
||||
--ks NONE \
|
||||
--ks-pass "pass:$YUBI_PIN" \
|
||||
--provider-class sun.security.pkcs11.SunPKCS11 \
|
||||
--provider-arg "${PROVIDER_CONFIG}" \
|
||||
--ks-type PKCS11 \
|
||||
--min-sdk-version "${MIN_SDK_VERSION}" \
|
||||
--max-sdk-version 34 \
|
||||
--in "${aligned_apk}" \
|
||||
--out "${signed_apk}" \
|
||||
--v1-signing-enabled \
|
||||
--v2-signing-enabled \
|
||||
--v3-signing-enabled \
|
||||
--v4-signing-enabled
|
||||
|
||||
# Safety check
|
||||
${APKSIGNER} verify --min-sdk-version "${MIN_SDK_VERSION}" "${signed_apk}"
|
||||
|
||||
# Remove temporary artifact
|
||||
rm "${aligned_apk}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Prevent leaking the PIN
|
||||
unset YUBI_PIN
|
||||
Reference in New Issue
Block a user