Skip to content

Commit

Permalink
nix: build unsigned Android APK, sign separately
Browse files Browse the repository at this point in the history
This has several benefits:

* Less abuse of `extra-sandbox-paths` Nix option
* Less inputs to the Android release build derivation
* Easier for users to sign the build themselves
* Simplification of `scripts/release-android.sh`
* Preparation for building using Nix Flakes

This still needs appropriate changes in CI builds.

Signed-off-by: Jakub Sokołowski <jakub@status.im>
  • Loading branch information
jakubgs committed Apr 22, 2022
1 parent 0c3097a commit 6605c06
Show file tree
Hide file tree
Showing 17 changed files with 80 additions and 126 deletions.
25 changes: 14 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -190,25 +190,28 @@ xcode-clean: ##@prepare Clean XCode derived data and archives
#----------------
release: release-android release-ios ##@build Build release for Android and iOS

release-android: export BUILD_ENV ?= prod
release-android: export BUILD_TYPE ?= nightly
release-android: export BUILD_NUMBER ?= $(TMP_BUILD_NUMBER)
build-android: SHELL := /bin/sh
build-android: export BUILD_ENV ?= prod
build-android: export BUILD_TYPE ?= nightly
build-android: export BUILD_NUMBER ?= $(TMP_BUILD_NUMBER)
build-android: export ANDROID_ABI_SPLIT ?= false
build-android: export ANDROID_ABI_INCLUDE ?= armeabi-v7a;arm64-v8a;x86
build-android: ##@build Build unsigned Android APK
@scripts/build-android.sh

release-android: export TARGET := keytool
release-android: export KEYSTORE_PATH ?= $(HOME)/.gradle/status-im.keystore
release-android: export ANDROID_APK_SIGNED ?= true
release-android: export ANDROID_ABI_SPLIT ?= false
release-android: export ANDROID_ABI_INCLUDE ?= armeabi-v7a;arm64-v8a;x86
release-android: keystore ##@build Build release for Android
scripts/release-android.sh
release-android: keystore build-android ##@build Build signed Android APK
@scripts/sign-android.sh result/app-release-unsigned.apk

release-fdroid: export BUILD_ENV = prod
release-fdroid: export BUILD_TYPE = release
release-fdroid: export ANDROID_APK_SIGNED = false
release-fdroid: export ANDROID_ABI_SPLIT = false
release-fdroid: export ANDROID_ABI_INCLUDE = armeabi-v7a;arm64-v8a;x86;x86_64
release-fdroid: export READER_FEATURES = google-free
release-fdroid: ##@build Build release for F-Droid
scripts/google-free.sh
scripts/release-android.sh
@scripts/google-free.sh
@scripts/build-android.sh

release-ios: export TARGET := ios
release-ios: export BUILD_ENV ?= prod
Expand Down
31 changes: 10 additions & 21 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -234,23 +234,6 @@ android {
jumboMode true
javaMaxHeapSize "8g"
}
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
/* Caution! In production, you need to generate your own keystore file.
* See: https://facebook.github.io/react-native/docs/signed-apk-android */
release {
/* environment variables take precedence over gradle.properties file */
storeFile file(getEnvOrConfig('KEYSTORE_PATH').replaceAll("~", System.properties['user.home']))
storePassword getEnvOrConfig('KEYSTORE_PASSWORD')
keyAlias getEnvOrConfig('KEYSTORE_ALIAS')
keyPassword getEnvOrConfig('KEYSTORE_KEY_PASSWORD')
}
}
splits {
abi {
reset()
Expand All @@ -259,20 +242,26 @@ android {
universalApk true
}
}
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
}
buildTypes {
debug {
applicationIdSuffix ".debug"
debuggable true
versionNameSuffix "-SNAPSHOT"
signingConfig signingConfigs.debug
resValue "string", "build_config_package", "im.status.ethereum"
signingConfig signingConfigs.debug
}
release {
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
if (getEnvOrConfig('ANDROID_APK_SIGNED').toBoolean()) {
signingConfig signingConfigs.release
}
signingConfig null
}
pr {
initWith release
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.31"
classpath "com.android.tools.build:gradle:${project.ext.gradlePluginVersion}"
classpath "com.google.gms:google-services:4.3.10"
classpath "com.google.gms:google-services:4.3.10"
// WARNING: Do not place your application dependencies here!
// They belong in the individual module build.gradle files.
}
Expand Down
2 changes: 0 additions & 2 deletions android/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ KEYSTORE_KEY_PASSWORD=password
ANDROID_ABI_SPLIT=false
# Some platforms are excluded though
ANDROID_ABI_INCLUDE=armeabi-v7a;arm64-v8a;x86
# F-Droid builds need to be unsigned
ANDROID_APK_SIGNED=true

org.gradle.jvmargs=-Xmx8704M

Expand Down
2 changes: 1 addition & 1 deletion ci/Jenkinsfile.android
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
library 'status-jenkins-lib@v1.4.0'
library 'status-jenkins-lib@build-unsigned-android'

pipeline {
agent { label 'linux && x86_64 && nix-2.6' }
Expand Down
2 changes: 1 addition & 1 deletion ci/Jenkinsfile.combined
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
library 'status-jenkins-lib@v1.4.0'
library 'status-jenkins-lib@build-unsigned-android'

pipeline {
agent { label 'linux' }
Expand Down
2 changes: 1 addition & 1 deletion ci/Jenkinsfile.ios
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
library 'status-jenkins-lib@v1.4.0'
library 'status-jenkins-lib@build-unsigned-android'

pipeline {
agent { label 'macos && x86_64 && nix-2.6 && xcode-12.5' }
Expand Down
2 changes: 1 addition & 1 deletion ci/Jenkinsfile.nix-cache
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
library 'status-jenkins-lib@v1.4.0'
library 'status-jenkins-lib@build-unsigned-android'

pipeline {
agent { label params.AGENT_LABEL }
Expand Down
1 change: 0 additions & 1 deletion nix/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ Here is a sample structure of the `config` attribute set:
build-number = 9999; # Used for versionCode and CFBundleVersion in Android and iOS respectively
android = {
gradle-opts = ""; # Gradle options passed for Android builds
keystore-path = ""; # Path to keystore for signing the APK
abi-split = false; # If APKs should be split based on architectures
abi-include = "x86"; # Android architectures to build for
};
Expand Down
1 change: 0 additions & 1 deletion nix/config.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

android = {
gradle-opts = null; # Gradle options passed for Android builds
keystore-path = null; # Path to keystore for signing the APK
apk-signed = true; # F-Droid builds aren't signed by us
abi-split = false; # If APKs should be split based on architectures
abi-include = "armeabi-v7a;arm64-v8a;x86"; # Android architectures to build for
Expand Down
7 changes: 2 additions & 5 deletions nix/mobile/android/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
, status-go, androidPkgs, androidShell }:

let
# For generating a temporary keystore for local development
keystore = callPackage ./keystore.nix { };

# Import a jsbundle compiled out of clojure codebase
jsbundle = callPackage ./jsbundle { };

Expand All @@ -13,12 +10,12 @@ let

# TARGETS
release = callPackage ./release.nix {
inherit keystore jsbundle status-go watchmanFactory;
inherit jsbundle status-go watchmanFactory;
};

in {
# TARGETS
inherit keystore release jsbundle;
inherit release jsbundle;

shell = mkShell {
buildInputs = with pkgs; [
Expand Down
44 changes: 0 additions & 44 deletions nix/mobile/android/keystore.nix

This file was deleted.

25 changes: 6 additions & 19 deletions nix/mobile/android/release.nix
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{ stdenv, pkgs, deps, lib, config, callPackage,
watchmanFactory, androidPkgs, patchMavenSources,
keystore, jsbundle, status-go }:
jsbundle, status-go }:

{
# Value for BUILD_ENV checked by Clojure code at compile time
Expand Down Expand Up @@ -28,9 +28,6 @@ let
gradleOpts = getConfig "android.gradle-opts" null;
# Used to detect end-to-end builds
androidAbiInclude = getConfig "android.abi-include" "armeabi-v7a;arm64-v8a;x86";
# Keystore can be provided via config and extra-sandbox-paths.
# If it is not we use an ad-hoc one generated with default password.
keystorePath = getConfig "android.keystore-path" keystore;

baseName = "${buildType}-android";
name = "status-react-build-${baseName}";
Expand Down Expand Up @@ -76,7 +73,6 @@ in stdenv.mkDerivation rec {

# custom env variables derived from config
STATUS_GO_SRC_OVERRIDE = getConfig "status-go.src-override" null;
ANDROID_APK_SIGNED = getConfig "android.apk-signed" "true";
ANDROID_ABI_SPLIT = getConfig "android.abi-split" "false";
ANDROID_ABI_INCLUDE = androidAbiInclude;

Expand All @@ -88,8 +84,7 @@ in stdenv.mkDerivation rec {
STATUS_GO_ANDROID_LIBDIR = status-go { inherit secretsFile; };

phases = [
"unpackPhase" "secretsPhase" "keystorePhase"
"buildPhase" "checkPhase" "installPhase"
"unpackPhase" "secretsPhase" "buildPhase" "checkPhase" "installPhase"
];

unpackPhase = ''
Expand Down Expand Up @@ -120,21 +115,13 @@ in stdenv.mkDerivation rec {
${patchMavenSources} ./android/build.gradle
'';

# if secretsFile is not set we use generate keystore
# Secrets file is passed to sandbox using extra-sandbox-paths.
secretsPhase = if (secretsFile != "") then ''
source "${secretsFile}"
${checkEnvVarSet "KEYSTORE_ALIAS"}
${checkEnvVarSet "KEYSTORE_PASSWORD"}
${checkEnvVarSet "KEYSTORE_KEY_PASSWORD"}
'' else keystore.shellHook;

# if keystorePath is set copy it into build directory
keystorePhase =
assert assertMsg (keystorePath != null) "keystorePath has to be set!";
''
export KEYSTORE_PATH="$PWD/status-im.keystore"
cp -a --no-preserve=ownership "${keystorePath}" "$KEYSTORE_PATH"
'' else ''
echo 'WARNING: No secrets provided!' >&2
'';

buildPhase = let
adhocEnvVars = optionalString stdenv.isLinux
"LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${makeLibraryPath [ pkgs.zlib ]}";
Expand Down
2 changes: 1 addition & 1 deletion nix/scripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ nixOpts=(
${GIT_ROOT}/nix/scripts/gcroots.sh "${TARGET}" "${@}"

# Run the actual build
echo "Running: nix-build "${nixOpts[@]}" "${@}" default.nix"
echo "${GRN}Running:${RST} nix-build "${nixOpts[@]}" "${@}" default.nix"
nixResultPath=$(nix-build "${nixOpts[@]}" "${@}" default.nix)

echo -e "\n${YLW}Extracting result${RST}: ${BLD}${nixResultPath}${RST}"
Expand Down
13 changes: 1 addition & 12 deletions scripts/release-android.sh → scripts/build-android.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,9 @@ config=''
if [[ -n "${STATUS_GO_SRC_OVERRIDE}" ]]; then
config+="status-im.status-go.src-override=\"${STATUS_GO_SRC_OVERRIDE}\";"
fi
if [[ "${ANDROID_APK_SIGNED}" == "true" ]]; then
config+="status-im.android.keystore-path=\"$(must_get_env KEYSTORE_PATH)\";"
fi
config+="status-im.commit-hash=\"$(git rev-parse --verify HEAD)\";"
config+="status-im.build-type=\"$(must_get_env BUILD_TYPE)\";"
config+="status-im.build-number=\"$(must_get_env BUILD_NUMBER)\";"
config+="status-im.android.apk-signed=\"$(must_get_env ANDROID_APK_SIGNED)\";"
config+="status-im.android.abi-split=\"$(must_get_env ANDROID_ABI_SPLIT)\";"
config+="status-im.android.abi-include=\"$(must_get_env ANDROID_ABI_INCLUDE)\";"
nixOpts=()
Expand All @@ -43,22 +39,15 @@ chmod 644 ${SECRETS_FILE_PATH}
# If secrets file was created we want to remove it.
trap "rm -vf ${SECRETS_FILE_PATH}" EXIT ERR INT QUIT
# Secrets like this can't be passed via args or they end up in derivation.
if [[ -n "${KEYSTORE_ALIAS}${KEYSTORE_ALIAS}${KEYSTORE_ALIAS}" ]]; then
# WARNING: All three have to be set!
append_env_export 'KEYSTORE_PASSWORD'
append_env_export 'KEYSTORE_ALIAS'
append_env_export 'KEYSTORE_KEY_PASSWORD'
fi
if [[ -n "${INFURA_TOKEN}" ]]; then
append_env_export 'INFURA_TOKEN'
fi
if [[ -n "${OPENSEA_API_KEY}" ]]; then
append_env_export 'OPENSEA_API_KEY'
fi

# Used by Clojure at compile time for remove import of react-native-notifications for fdroid release
if [[ -n "${READER_FEATURES}" ]]; then
append_env_export 'READER_FEATURES'
append_env_export 'READER_FEATURES'
fi

# If no secrets were passed there's no need to pass the 'secretsFile'.
Expand Down
5 changes: 1 addition & 4 deletions scripts/generate-keystore.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ KEYSTORE_PATH=${KEYSTORE_PATH/#\~/$HOME}

if [[ -e "${KEYSTORE_PATH}" ]]; then
echo -e "${YLW}Keystore file already exists:${RST} ${KEYSTORE_PATH}" >&2
echo "${KEYSTORE_PATH}"
exit 0
fi

Expand All @@ -41,7 +40,7 @@ KEYSTORE_DIR=$(dirname "${KEYSTORE_PATH}")

echo -e "${GRN}Generating keystore...${RST}" >&2

keytool -genkey -v \
exec keytool -genkey -v \
-keyalg RSA \
-keysize 2048 \
-validity 10000 \
Expand All @@ -52,5 +51,3 @@ keytool -genkey -v \
-storepass "${KEYSTORE_PASSWORD}" \
-keypass "${KEYSTORE_KEY_PASSWORD}" \
> /dev/stderr

echo "${KEYSTORE_PATH}"
40 changes: 40 additions & 0 deletions scripts/sign-android.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -e

GIT_ROOT=$(cd "${BASH_SOURCE%/*}" && git rev-parse --show-toplevel)
source "${GIT_ROOT}/scripts/colors.sh"

function property() {
grep "${2}" "${1}" | cut -d'=' -f2
}

function gradle_property() {
property ${GIT_ROOT}/android/gradle.properties ${1}
}

function env_var_or_gradle_prop() {
VAR_NAME="${1}"
if [[ -n "${!VAR_NAME}" ]]; then
echo "${!VAR_NAME}"
else
gradle_property "${VAR_NAME}"
fi
}

function must_get_env() {
declare -n VAR_VALUE="$1"
if [[ -n "${VAR_VALUE}" ]]; then
echo "${VAR_VALUE}"
return
fi
echo -e "${RED}No required env variable:${RST} ${BLD}${!VAR_VALUE}${RST}" 1>&2
exit 1
}

echo -e "${GRN}Signing APK:${RST} ${@}" >&2

exec jarsigner \
-keystore "$(env_var_or_gradle_prop KEYSTORE_PATH)" \
-storepass "$(env_var_or_gradle_prop KEYSTORE_PASSWORD)" \
-keypass "$(env_var_or_gradle_prop KEYSTORE_KEY_PASSWORD)" \
"${@}" "$(env_var_or_gradle_prop KEYSTORE_ALIAS)"

0 comments on commit 6605c06

Please sign in to comment.