diff --git a/nix/README.md b/nix/README.md index 0643f57386..67171f2e9a 100644 --- a/nix/README.md +++ b/nix/README.md @@ -31,6 +31,7 @@ config = { }; }; ``` +You can see the defaults in [`nix/config.nix`](./config.nix). ## Shell diff --git a/nix/config.nix b/nix/config.nix new file mode 100644 index 0000000000..a17fa8b221 --- /dev/null +++ b/nix/config.nix @@ -0,0 +1,22 @@ +# Status defaults for config +{ + status-im = { + build-type = "pr"; # Build type (influences which .env file gets used for feature flags) + build-number = 9999; # Used for versionCode and CFBundleVersion in Android and iOS respectively + + android = { + gradle-opts = null; # Gradle options passed for Android builds + keystore-path = null; # Path to keystore for signing the APK + abi-split = false; # If APKs should be split based on architectures + abi-include = "armeabi-v7a;arm64-v8a;x86"; # Android architectures to build for + }; + + nimbus = { src-override = null; }; + status-go = { src-override = null; }; + }; + + # Android SDK requires an accepted license + android_sdk.accept_license = true; + # Android Env still needs old OpenSSL + permittedInsecurePackages = [ "openssl-1.0.2u" ]; +} diff --git a/nix/lib/assertEnvVarSet.nix b/nix/lib/assertEnvVarSet.nix new file mode 100644 index 0000000000..0c7aea2b11 --- /dev/null +++ b/nix/lib/assertEnvVarSet.nix @@ -0,0 +1,7 @@ +# Helper for verifying an environment variable is set +name: '' + if [[ -z ''$${name} ]]; then + echo 'Not env var set: ${name}' >&2 + exit 1 + fi +'' diff --git a/nix/lib/default.nix b/nix/lib/default.nix index 57543587c5..841c59a387 100644 --- a/nix/lib/default.nix +++ b/nix/lib/default.nix @@ -4,4 +4,5 @@ getConfig = import ./getConfig.nix { inherit lib config; }; mkFilter = import ./mkFilter.nix { inherit lib; }; mergeSh = import ./mergeSh.nix { inherit lib; }; + assertEnvVarSet = import ./assertEnvVarSet.nix; } diff --git a/nix/mobile/android/default.nix b/nix/mobile/android/default.nix index 5f017975c9..7d3ab07b3e 100644 --- a/nix/mobile/android/default.nix +++ b/nix/mobile/android/default.nix @@ -2,6 +2,9 @@ , 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 { }; @@ -10,12 +13,12 @@ let # TARGETS release = callPackage ./release.nix { - inherit jsbundle status-go watchmanFactory; + inherit keystore jsbundle status-go watchmanFactory; }; in { # TARGETS - inherit release jsbundle; + inherit keystore release jsbundle; shell = mkShell { buildInputs = with pkgs; [ diff --git a/nix/mobile/android/jsbundle/default.nix b/nix/mobile/android/jsbundle/default.nix index ec61fdfbd1..108f06903c 100644 --- a/nix/mobile/android/jsbundle/default.nix +++ b/nix/mobile/android/jsbundle/default.nix @@ -22,9 +22,9 @@ stdenv.mkDerivation { ignoreVCS = false; include = [ "VERSION" "BUILD_NUMBER" "scripts/version/.*" + "src/.*" "shadow-cljs.edn" "index.js" # I want to avoid including the whole .git directory ".git/HEAD" ".git/objects" ".git/refs/heads/.*" - "src/.*" "shadow-cljs.edn" "index.js" # shadow-cljs expects these for deps resolution "mobile/js_files/package.json" "mobile/js_files/yarn.lock" # build stat's images to check if they exist diff --git a/nix/mobile/android/keystore.nix b/nix/mobile/android/keystore.nix new file mode 100644 index 0000000000..a6d4a5d171 --- /dev/null +++ b/nix/mobile/android/keystore.nix @@ -0,0 +1,44 @@ +# +# Generates an ad-hoc and temporary keystore for signing debug/pr builds. +# +# WARNING: Do NOT use this to make a keystore that needs to be secret! +# Using a derivation will store the inputs in a .drv file. +# +{ stdenv, lib, pkgs }: + +let + inherit (lib) getAttr; + + gradleProps = pkgs.gradlePropParser ../../../android/gradle.properties; + + # Loading defaults from gradle.properties which should be safe. + KEYSTORE_ALIAS = getAttr "KEYSTORE_ALIAS" gradleProps; + KEYSTORE_PASSWORD = getAttr "KEYSTORE_PASSWORD" gradleProps; + KEYSTORE_KEY_PASSWORD = getAttr "KEYSTORE_KEY_PASSWORD" gradleProps; + +in stdenv.mkDerivation { + name = "status-react-android-keystore"; + + buildInputs = [ pkgs.openjdk8 ]; + + phases = [ "generatePhase" ]; + generatePhase = '' + keytool -genkey -v \ + -keyalg RSA \ + -keysize 2048 \ + -validity 10000 \ + -deststoretype pkcs12 \ + -dname "CN=, OU=, O=, L=, S=, C=" \ + -keystore "$out" \ + -alias "${KEYSTORE_ALIAS}" \ + -storepass "${KEYSTORE_PASSWORD}" \ + -keypass "${KEYSTORE_KEY_PASSWORD}" \ + >&2 + ''; + + shellHook = '' + export KEYSTORE_ALIAS="${KEYSTORE_ALIAS}" + export KEYSTORE_PASSWORD="${KEYSTORE_PASSWORD}" + export KEYSTORE_KEY_PASSWORD="${KEYSTORE_KEY_PASSWORD}" + ''; +} diff --git a/nix/mobile/android/release.nix b/nix/mobile/android/release.nix index 3894cbedcf..b6511ad4bf 100644 --- a/nix/mobile/android/release.nix +++ b/nix/mobile/android/release.nix @@ -1,22 +1,30 @@ { stdenv, pkgs, deps, lib, config, callPackage, watchmanFactory, androidPkgs, patchMavenSources, - jsbundle, status-go }: + keystore, jsbundle, status-go }: { - buildEnv ? "prod", # Value for BUILD_ENV checked by Clojure code at compile time - secretsFile ? "", # Path to the file containing secret environment variables - watchmanSockPath ? "", # Path to the socket file exposed by an external watchman instance (workaround needed for building Android on macOS) + # Value for BUILD_ENV checked by Clojure code at compile time + buildEnv ? "prod", + # Path to the file containing secret environment variables + secretsFile ? "", + # Path to the socket file exposed by an external watchman instance + # (workaround needed for building Android on macOS) + watchmanSockPath ? "", }: assert (lib.stringLength watchmanSockPath) > 0 -> stdenv.isDarwin; let - inherit (lib) toLower optionalString stringLength getConfig makeLibraryPath; + inherit (lib) + toLower optionalString stringLength + getConfig makeLibraryPath assertEnvVarSet; buildType = getConfig "build-type" "prod"; buildNumber = getConfig "build-number" 9999; gradleOpts = getConfig "android.gradle-opts" null; - keystorePath = getConfig "android.keystore-path" null; + # 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 = "release-android"; name = "status-react-build-${baseName}"; @@ -73,7 +81,8 @@ in stdenv.mkDerivation rec { STATUS_GO_ANDROID_LIBDIR = "${status-go}"; phases = [ - "unpackPhase" "secretPhase" "buildPhase" "checkPhase" "installPhase" + "unpackPhase" "secretsPhase" "secretsCheckPhase" + "buildPhase" "checkPhase" "installPhase" ]; unpackPhase = '' @@ -81,7 +90,9 @@ in stdenv.mkDerivation rec { chmod u+w -R ./ runHook postUnpack ''; - postUnpack = assert lib.assertMsg (keystorePath != null) "keystore-file has to be set!"; '' + postUnpack = + assert lib.assertMsg (keystorePath != null) "keystore-file has to be set!"; + '' # Keep the same keystore path for determinism export KEYSTORE_PATH="$PWD/status-im.keystore" cp -a --no-preserve=ownership "${keystorePath}" "$KEYSTORE_PATH" @@ -107,8 +118,14 @@ in stdenv.mkDerivation rec { # Patch build.gradle to use local repo ${patchMavenSources} ./android/build.gradle ''; - secretPhase = optionalString (secretsFile != "") '' + # if secretsFile is not set we use generate keystore + secretsPhase = if (secretsFile != "") then '' source "${secretsFile}" + '' else keystore.shellHook; + secretsCheckPhase = '' + ${assertEnvVarSet "KEYSTORE_ALIAS"} + ${assertEnvVarSet "KEYSTORE_PASSWORD"} + ${assertEnvVarSet "KEYSTORE_KEY_PASSWORD"} ''; buildPhase = let adhocEnvVars = optionalString stdenv.isLinux diff --git a/nix/overlay.nix b/nix/overlay.nix index 22e43511ae..a0f925ed5f 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -28,8 +28,8 @@ in { react-native = callPackage ./deps/react-native { }; }; - # For patching Node.js modules with Gradle repo path - patchNodeModules = callPackage ./tools/patchNodeModules.nix { }; + # For parsing gradle.properties into an attrset + gradlePropParser = callPackage ./tools/gradlePropParser.nix { }; # Package version adjustments xcodeWrapper = super.xcodeenv.composeXcodeWrapper { version = "11.4.1"; }; diff --git a/nix/pkgs.nix b/nix/pkgs.nix index de9dcb51b2..1c969cc3cd 100644 --- a/nix/pkgs.nix +++ b/nix/pkgs.nix @@ -19,16 +19,11 @@ let # nix-prefetch-url --unpack https://github.com/${ORG}/nixpkgs/archive/${REV}.tar.gz }; + # Status specific configuration defaults + defaultConfig = import ./config.nix; + # Override some packages and utilities pkgsOverlay = import ./overlay.nix; - - # we want these packages easily available via callPackage - defaultConfig = { - android_sdk.accept_license = true; - # Android Env still needs old OpenSSL - permittedInsecurePackages = [ "openssl-1.0.2u" ]; - }; - in # import nixpkgs with a config override (import nixpkgsSrc) { diff --git a/nix/tools/gradlePropParser.nix b/nix/tools/gradlePropParser.nix new file mode 100644 index 0000000000..483e2b2b94 --- /dev/null +++ b/nix/tools/gradlePropParser.nix @@ -0,0 +1,20 @@ +# Parser for android/gradle.properties file. +# Returns an attrset with keys and values from it. +{ lib }: + +gradlePropsFile: + +let + inherit (lib) head last filter listToAttrs splitString nameValuePair hasPrefix readFile; + + # Read lines + lines = splitString "\n" (readFile gradlePropsFile); + + isKeyValueLine = line: line != "" && !hasPrefix "#" line; + cleanup = lines: filter isKeyValueLine lines; + extractKeyValues = line: + let flag = splitString "=" line; + in nameValuePair (head flag) (last flag); + parseAttrs = lines: listToAttrs (map extractKeyValues lines); +in + parseAttrs (cleanup lines) diff --git a/scripts/generate-keystore.sh b/scripts/generate-keystore.sh index c017ed9c0d..592a0c14c3 100755 --- a/scripts/generate-keystore.sh +++ b/scripts/generate-keystore.sh @@ -45,6 +45,7 @@ keytool -genkey -v \ -keyalg RSA \ -keysize 2048 \ -validity 10000 \ + -deststoretype pkcs12 \ -dname "CN=, OU=, O=, L=, S=, C=" \ -keystore "${KEYSTORE_PATH}" \ -alias "${KEYSTORE_ALIAS}" \