diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 46abae8e8..83a9fcf9f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - FLUTTER_VERSION: 3.0.2 + FLUTTER_VERSION: 3.0.4 image: cirrusci/flutter:${FLUTTER_VERSION} @@ -31,6 +31,7 @@ test: stage: coverage script: [flutter test] +# the basic integration test configuration testing FLOSS builds on Synapse integration_test: image: registry.gitlab.com/famedly/company/frontend/flutter-dockerimages/integration/stable:${FLUTTER_VERSION} stage: coverage @@ -47,29 +48,78 @@ integration_test: # Disable TLS since we're running inside local network. DOCKER_TLS_CERTDIR: "" before_script: - - chmod 777 -R /dev/kvm - - adb start-server - - emulator -avd test -no-audio -no-boot-anim -no-window -accel on -gpu swiftshader_indirect & - - apk update && apk add docker drill grep - - chown -R 991:991 integration_test/synapse - - docker run -d --name synapse --user 991:991 --network=host --volume="$(pwd)/integration_test/synapse/data":/data:rw -p 8008:8008 matrixdotorg/synapse:latest - - sleep 20 - # create three test user - - docker exec -i synapse register_new_matrix_user http://localhost:8008 -c /data/homeserver.yaml --user alice --password AliceInWonderland --admin - - docker exec -i synapse register_new_matrix_user http://localhost:8008 -c /data/homeserver.yaml --user bob --password JoWirSchaffenDas --admin - - docker exec -i synapse register_new_matrix_user http://localhost:8008 -c /data/homeserver.yaml --user trudy --password HaveIBeenPwned --admin - script: - # properly set the homeserver IP for the test - - sed -i "s/10.0.2.2/$(drill docker | grep -m 1 -P "\d+\.\d+\.\d+.\d+" | awk -F ' ' '{print $NF}')/g" integration_test/users.dart + # start AVD and keep running in background + - scripts/integration-start-avd.sh & + - scripts/integration-prepare-alpine.sh + # create Synapse instance + - scripts/integration-server-synapse.sh + # properly set the homeserver IP and create test users + - scripts/integration-prepare-homeserver.sh + # ensure the homeserver works - curl docker:8008/_matrix/static/ 2> /dev/null | grep "It works! Synapse is running" + script: + - flutter pub get + - flutter test integration_test + tags: + - famedly + - docker + timeout: 20m + +# extending the default tests to test the Google-flavored builds +integration_test_google: + extends: integration_test + script: - git apply ./scripts/enable-android-google-services.patch - flutter pub get - flutter test integration_test + +# extending the default tests to use Conduit as local homeserver +integration_test_conduit: + extends: integration_test + before_script: + # start AVD and keep running in background + - scripts/integration-start-avd.sh & + - scripts/integration-prepare-alpine.sh + # create Conduit instance + - scripts/integration-server-conduit.sh + # properly set the homeserver IP and create test users + - scripts/integration-prepare-homeserver.sh + # ensure the homeserver works + - curl docker:8008/_matrix/static/ 2> /dev/null | grep "M_NOT_FOUND" 1> /dev/null && echo "Conduit is running!" + +# extending the default tests to use Dendrite as local homeserver +integration_test_dendrite: + extends: integration_test + before_script: + # start AVD and keep running in background + - scripts/integration-start-avd.sh & + - scripts/integration-prepare-alpine.sh + # create Dendrite instance + - scripts/integration-server-dendrite.sh + # properly set the homeserver IP and create test users + - scripts/integration-prepare-homeserver.sh + # ensure the homeserver works + - curl docker:8008/_matrix/static/ 2> /dev/null | grep "404 page not found" 1> /dev/null && echo "Dendrite is running!" + +release_mode_launches: + image: registry.gitlab.com/famedly/company/frontend/flutter-dockerimages/integration/stable:${FLUTTER_VERSION} + stage: coverage + before_script: + script: + # start AVD and keep running in background + - scripts/integration-start-avd.sh & + # generate temporary release build configuration and ensure app launches + - scripts/integration-check-release-build.sh tags: - famedly - docker timeout: 20m +release_mode_launches_google: + extends: release_mode_launches + before_script: + - git apply ./scripts/enable-android-google-services.patch + build_web: stage: coverage before_script: @@ -109,7 +159,9 @@ build_android_debug: build_android_apk: stage: coverage - before_script: [./scripts/prepare-android-release.sh] + before_script: + - git apply ./scripts/enable-android-google-services.patch + - ./scripts/prepare-android-release.sh script: [./scripts/build-android-apk.sh] artifacts: when: on_success @@ -121,7 +173,9 @@ build_android_apk: build_android_appbundle: stage: coverage - before_script: [./scripts/prepare-android-release.sh] + before_script: + - git apply ./scripts/enable-android-google-services.patch + - ./scripts/prepare-android-release.sh script: [./scripts/release-playstore-beta.sh] artifacts: when: on_success @@ -339,7 +393,9 @@ upload-windows: upload-playstore: stage: release - before_script: [./scripts/prepare-android-release.sh] + before_script: + - git apply ./scripts/enable-android-google-services.patch + - ./scripts/prepare-android-release.sh script: [./scripts/release-playstore.sh] resource_group: playstore_release only: diff --git a/integration_test/.gitignore b/integration_test/.gitignore index 539d882e4..3284fe008 100644 --- a/integration_test/.gitignore +++ b/integration_test/.gitignore @@ -1 +1,6 @@ synapse/data/homeserver.db +dendrite/data/server.* +dendrite/data/matrix_key.pem +dendrite/data/logs +dendrite/data/jetstream +dendrite/data/*.db \ No newline at end of file diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart index 0ba99b214..41e60a3b1 100644 --- a/integration_test/app_test.dart +++ b/integration_test/app_test.dart @@ -2,7 +2,6 @@ import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:http/http.dart'; import 'package:integration_test/integration_test.dart'; import 'package:fluffychat/main.dart' as app; @@ -13,10 +12,6 @@ void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('Integration Test', () { - test('Check server availability', () async { - final response = await get(Uri.parse('$homeserver/_matrix/static/')); - expect(response.statusCode, 200); - }, timeout: const Timeout(Duration(seconds: 10))); testWidgets('Test if the app starts', (WidgetTester tester) async { app.main(); await tester.pumpAndSettle(); diff --git a/integration_test/dendrite/data/dendrite.yaml b/integration_test/dendrite/data/dendrite.yaml new file mode 100644 index 000000000..f1e95fd4b --- /dev/null +++ b/integration_test/dendrite/data/dendrite.yaml @@ -0,0 +1,327 @@ +# This is the Dendrite configuration file. +# +# The configuration is split up into sections - each Dendrite component has a +# configuration section, in addition to the "global" section which applies to +# all components. + +# The version of the configuration file. +version: 2 + +# Global Matrix configuration. This configuration applies to all components. +global: + # The domain name of this homeserver. + server_name: localhost + + # The path to the signing private key file, used to sign requests and events. + # Note that this is NOT the same private key as used for TLS! To generate a + # signing key, use "./bin/generate-keys --private-key matrix_key.pem". + private_key: matrix_key.pem + + # The paths and expiry timestamps (as a UNIX timestamp in millisecond precision) + # to old signing private keys that were formerly in use on this domain. These + # keys will not be used for federation request or event signing, but will be + # provided to any other homeserver that asks when trying to verify old events. + old_private_keys: + # - private_key: old_matrix_key.pem + # expired_at: 1601024554498 + + # How long a remote server can cache our server signing key before requesting it + # again. Increasing this number will reduce the number of requests made by other + # servers for our key but increases the period that a compromised key will be + # considered valid by other homeservers. + key_validity_period: 168h0m0s + + # Global database connection pool, for PostgreSQL monolith deployments only. If + # this section is populated then you can omit the "database" blocks in all other + # sections. For polylith deployments, or monolith deployments using SQLite databases, + # you must configure the "database" block for each component instead. + database: + connection_string: + max_open_conns: + max_idle_conns: + conn_max_lifetime: + + # Configuration for in-memory caches. Caches can often improve performance by + # keeping frequently accessed items (like events, identifiers etc.) in memory + # rather than having to read them from the database. + cache: + # The estimated maximum size for the global cache in bytes, or in terabytes, + # gigabytes, megabytes or kilobytes when the appropriate 'tb', 'gb', 'mb' or + # 'kb' suffix is specified. Note that this is not a hard limit, nor is it a + # memory limit for the entire process. A cache that is too small may ultimately + # provide little or no benefit. + max_size_estimated: 1gb + + # The maximum amount of time that a cache entry can live for in memory before + # it will be evicted and/or refreshed from the database. Lower values result in + # easier admission of new cache entries but may also increase database load in + # comparison to higher values, so adjust conservatively. Higher values may make + # it harder for new items to make it into the cache, e.g. if new rooms suddenly + # become popular. + max_age: 1h + + # The server name to delegate server-server communications to, with optional port + # e.g. localhost:443 + well_known_server_name: "" + + # Lists of domains that the server will trust as identity servers to verify third + # party identifiers such as phone numbers and email addresses. + trusted_third_party_id_servers: + - matrix.org + - vector.im + + # Disables federation. Dendrite will not be able to communicate with other servers + # in the Matrix federation and the federation API will not be exposed. + disable_federation: false + + # Configures the handling of presence events. Inbound controls whether we receive + # presence events from other servers, outbound controls whether we send presence + # events for our local users to other servers. + presence: + enable_inbound: false + enable_outbound: false + + # Configures phone-home statistics reporting. These statistics contain the server + # name, number of active users and some information on your deployment config. + # We use this information to understand how Dendrite is being used in the wild. + report_stats: + enabled: false + endpoint: https://matrix.org/report-usage-stats/push + + # Server notices allows server admins to send messages to all users on the server. + server_notices: + enabled: false + # The local part, display name and avatar URL (as a mxc:// URL) for the user that + # will send the server notices. These are visible to all users on the deployment. + local_part: "_server" + display_name: "Server Alerts" + avatar_url: "" + # The room name to be used when sending server notices. This room name will + # appear in user clients. + room_name: "Server Alerts" + + # Configuration for NATS JetStream + jetstream: + # A list of NATS Server addresses to connect to. If none are specified, an + # internal NATS server will be started automatically when running Dendrite in + # monolith mode. For polylith deployments, it is required to specify the address + # of at least one NATS Server node. + addresses: + # - localhost:4222 + + # Persistent directory to store JetStream streams in. This directory should be + # preserved across Dendrite restarts. + storage_path: ./ + + # The prefix to use for stream names for this homeserver - really only useful + # if you are running more than one Dendrite server on the same NATS deployment. + topic_prefix: Dendrite + + # Configuration for Prometheus metric collection. + metrics: + enabled: false + basic_auth: + username: metrics + password: metrics + + # Optional DNS cache. The DNS cache may reduce the load on DNS servers if there + # is no local caching resolver available for use. + dns_cache: + enabled: false + cache_size: 256 + cache_lifetime: "5m" # 5 minutes; https://pkg.go.dev/time@master#ParseDuration + +# Configuration for the Appservice API. +app_service_api: + database: + connection_string: file:app_service_api.db + + # Disable the validation of TLS certificates of appservices. This is + # not recommended in production since it may allow appservice traffic + # to be sent to an insecure endpoint. + disable_tls_validation: true + + # Appservice configuration files to load into this homeserver. + config_files: + # - /path/to/appservice_registration.yaml + +# Configuration for the Client API. +client_api: + # Prevents new users from being able to register on this homeserver, except when + # using the registration shared secret below. + registration_disabled: false + + # Prevents new guest accounts from being created. Guest registration is also + # disabled implicitly by setting 'registration_disabled' above. + guests_disabled: true + + # If set, allows registration by anyone who knows the shared secret, regardless + # of whether registration is otherwise disabled. + registration_shared_secret: "" + + # Whether to require reCAPTCHA for registration. If you have enabled registration + # then this is HIGHLY RECOMMENDED to reduce the risk of your homeserver being used + # for coordinated spam attacks. + enable_registration_captcha: false + + # Settings for ReCAPTCHA. + recaptcha_public_key: "" + recaptcha_private_key: "" + recaptcha_bypass_secret: "" + recaptcha_siteverify_api: "" + + # TURN server information that this homeserver should send to clients. + turn: + turn_user_lifetime: "" + turn_uris: + # - turn:turn.server.org?transport=udp + # - turn:turn.server.org?transport=tcp + turn_shared_secret: "" + turn_username: "" + turn_password: "" + + # Settings for rate-limited endpoints. Rate limiting kicks in after the threshold + # number of "slots" have been taken by requests from a specific host. Each "slot" + # will be released after the cooloff time in milliseconds. Server administrators + # and appservice users are exempt from rate limiting by default. + rate_limiting: + enabled: true + threshold: 5 + cooloff_ms: 500 + exempt_user_ids: + # - "@user:domain.com" + +# Configuration for the Federation API. +federation_api: + database: + connection_string: file:federation_api.db + + # How many times we will try to resend a failed transaction to a specific server. The + # backoff is 2**x seconds, so 1 = 2 seconds, 2 = 4 seconds, 3 = 8 seconds etc. Once + # the max retries are exceeded, Dendrite will no longer try to send transactions to + # that server until it comes back to life and connects to us again. + send_max_retries: 16 + + # Disable the validation of TLS certificates of remote federated homeservers. Do not + # enable this option in production as it presents a security risk! + disable_tls_validation: false + + # Perspective keyservers to use as a backup when direct key fetches fail. This may + # be required to satisfy key requests for servers that are no longer online when + # joining some rooms. + key_perspectives: + - server_name: matrix.org + keys: + - key_id: ed25519:auto + public_key: Noi6WqcDj0QmPxCNQqgezwTlBKrfqehY1u2FyWP9uYw + - key_id: ed25519:a_RXGa + public_key: l8Hft5qXKn1vfHrg3p4+W8gELQVo8N13JkluMfmn2sQ + + # This option will control whether Dendrite will prefer to look up keys directly + # or whether it should try perspective servers first, using direct fetches as a + # last resort. + prefer_direct_fetch: false + +# Configuration for the Media API. +media_api: + database: + connection_string: file:media_api.db + + # Storage path for uploaded media. May be relative or absolute. + base_path: ./media_store + + # The maximum allowed file size (in bytes) for media uploads to this homeserver + # (0 = unlimited). If using a reverse proxy, ensure it allows requests at least + #this large (e.g. the client_max_body_size setting in nginx). + max_file_size_bytes: 10485760 + + # Whether to dynamically generate thumbnails if needed. + dynamic_thumbnails: false + + # The maximum number of simultaneous thumbnail generators to run. + max_thumbnail_generators: 10 + + # A list of thumbnail sizes to be generated for media content. + thumbnail_sizes: + - width: 32 + height: 32 + method: crop + - width: 96 + height: 96 + method: crop + - width: 640 + height: 480 + method: scale + +# Configuration for enabling experimental MSCs on this homeserver. +mscs: + database: + connection_string: file:mscs.db + mscs: + # - msc2836 # (Threading, see https://github.com/matrix-org/matrix-doc/pull/2836) + # - msc2946 # (Spaces Summary, see https://github.com/matrix-org/matrix-doc/pull/2946) + +# Configuration for the Sync API. +sync_api: + # This option controls which HTTP header to inspect to find the real remote IP + # address of the client. This is likely required if Dendrite is running behind + # a reverse proxy server. + # real_ip_header: X-Real-IP + database: + connection_string: file:sync_api.db + +key_server: + database: + connection_string: file:key_server.db + +room_server: + database: + connection_string: file:room_server.db + + +# Configuration for the User API. +user_api: + account_database: + connection_string: file:user_api.db + + # The cost when hashing passwords on registration/login. Default: 10. Min: 4, Max: 31 + # See https://pkg.go.dev/golang.org/x/crypto/bcrypt for more information. + # Setting this lower makes registration/login consume less CPU resources at the cost + # of security should the database be compromised. Setting this higher makes registration/login + # consume more CPU resources but makes it harder to brute force password hashes. This value + # can be lowered if performing tests or on embedded Dendrite instances (e.g WASM builds). + bcrypt_cost: 10 + + # The length of time that a token issued for a relying party from + # /_matrix/client/r0/user/{userId}/openid/request_token endpoint + # is considered to be valid in milliseconds. + # The default lifetime is 3600000ms (60 minutes). + # openid_token_lifetime_ms: 3600000 + +# Configuration for Opentracing. +# See https://github.com/matrix-org/dendrite/tree/master/docs/tracing for information on +# how this works and how to set it up. +tracing: + enabled: false + jaeger: + serviceName: "" + disabled: false + rpc_metrics: false + tags: [] + sampler: null + reporter: null + headers: null + baggage_restrictions: null + throttler: null + +# Logging configuration. The "std" logging type controls the logs being sent to +# stdout. The "file" logging type controls logs being written to a log folder on +# the disk. Supported log levels are "debug", "info", "warn", "error". +logging: + - type: std + level: info + - type: file + level: info + params: + path: ./logs + diff --git a/lib/pages/login/login.dart b/lib/pages/login/login.dart index 530fccafc..a46db2481 100644 --- a/lib/pages/login/login.dart +++ b/lib/pages/login/login.dart @@ -94,7 +94,7 @@ class LoginController extends State { } void _checkWellKnown(String userId) async { - setState(() => usernameError = null); + if (mounted) setState(() => usernameError = null); if (!userId.isValidMatrixId) return; try { final oldHomeserver = Matrix.of(context).getLoginClient().homeserver; @@ -133,19 +133,21 @@ class LoginController extends State { cancelLabel: L10n.of(context)!.cancel, ); if (dialogResult == OkCancelResult.ok) { - setState(() => usernameError = null); + if (mounted) setState(() => usernameError = null); } else { Navigator.of(context, rootNavigator: false).pop(); return; } } - setState(() => usernameError = null); + if (mounted) setState(() => usernameError = null); } else { - setState(() => - Matrix.of(context).getLoginClient().homeserver = oldHomeserver); + if (mounted) { + setState(() => + Matrix.of(context).getLoginClient().homeserver = oldHomeserver); + } } } catch (e) { - setState(() => usernameError = e.toString()); + if (mounted) setState(() => usernameError = e.toString()); } } @@ -243,6 +245,8 @@ class LoginController extends State { extension on String { static final RegExp _phoneRegex = RegExp(r'^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*$'); + bool get isEmail => EmailValidator.validate(this); + bool get isPhoneNumber => _phoneRegex.hasMatch(this); } diff --git a/scripts/integration-check-release-build.sh b/scripts/integration-check-release-build.sh new file mode 100755 index 000000000..5e329189f --- /dev/null +++ b/scripts/integration-check-release-build.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# generate a temporary signing key adn apply its configuration +cd android +KEYFILE="$(pwd)/key.jks" +echo "Generating signing configuration with $KEYFILE..." +keytool -genkey -keyalg RSA -alias key -keysize 4096 -dname "cn=FluffyChat CI, ou=Head of bad integration tests, o=FluffyChat HQ, c=TLH" -keypass FLUFFYCHAT -storepass FLUFFYCHAT -validity 1 -keystore "$KEYFILE" -storetype "pkcs12" +echo "storePassword=FLUFFYCHAT" >> key.properties +echo "keyPassword=FLUFFYCHAT" >> key.properties +echo "keyAlias=key" >> key.properties +echo "storeFile=$KEYFILE" >> key.properties +ls | grep key +cd .. + +# build release mode APK +flutter pub get +flutter build apk --release + +# install and launch APK +flutter install +adb shell am start -n chat.fluffy.fluffychat/chat.fluffy.fluffychat.MainActivity + +sleep 5 + +# check whether FluffyChat runs +adb shell ps | awk '{print $9}' | grep chat.fluffy.fluffychat diff --git a/scripts/integration-prepare-alpine.sh b/scripts/integration-prepare-alpine.sh new file mode 100755 index 000000000..f4a57b6d5 --- /dev/null +++ b/scripts/integration-prepare-alpine.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +apk update && apk add docker drill grep \ No newline at end of file diff --git a/scripts/integration-prepare-homeserver.sh b/scripts/integration-prepare-homeserver.sh new file mode 100755 index 000000000..70fcb2f72 --- /dev/null +++ b/scripts/integration-prepare-homeserver.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +IP_ADDRESS="$(drill docker | grep -m 1 -P "\d+\.\d+\.\d+.\d+" | awk -F ' ' '{print $NF}')" + +sed -i "s/10.0.2.2/$IP_ADDRESS/g" integration_test/users.dart + +curl -XPOST -d '{"username":"alice", "password":"AliceInWonderland", "inhibit_login":true, "auth": {"type":"m.login.dummy"}}' "http://$IP_ADDRESS:8008/_matrix/client/r0/register" +curl -XPOST -d '{"username":"bob", "password":"JoWirSchaffenDas", "inhibit_login":true, "auth": {"type":"m.login.dummy"}}' "http://$IP_ADDRESS:8008/_matrix/client/r0/register" +curl -XPOST -d '{"username":"trudy", "password":"HaveIBeenPwned", "inhibit_login":true, "auth": {"type":"m.login.dummy"}}' "http://$IP_ADDRESS:8008/_matrix/client/r0/register" \ No newline at end of file diff --git a/scripts/integration-server-conduit.sh b/scripts/integration-server-conduit.sh new file mode 100755 index 000000000..7e8ab7c6b --- /dev/null +++ b/scripts/integration-server-conduit.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + docker run -d \ + -e CONDUIT_SERVER_NAME="localhost" \ + -e CONDUIT_PORT="8008" \ + -e CONDUIT_DATABASE_BACKEND="rocksdb" \ + -e CONDUIT_ALLOW_REGISTRATION=true \ + -e CONDUIT_ALLOW_FEDERATION=true \ + -e CONDUIT_MAX_REQUEST_SIZE="20000000" \ + -e CONDUIT_TRUSTED_SERVERS="[\"conduit.rs\"]" \ + -e CONDUIT_MAX_CONCURRENT_REQUESTS="100" \ + -e CONDUIT_LOG="info,rocket=off,_=off,sled=off" \ + --name conduit -p 8008:8008 matrixconduit/matrix-conduit:latest + +sleep 20 \ No newline at end of file diff --git a/scripts/integration-server-dendrite.sh b/scripts/integration-server-dendrite.sh new file mode 100755 index 000000000..d7389141b --- /dev/null +++ b/scripts/integration-server-dendrite.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +chown -R 991:991 integration_test/dendrite + +# creating integration test SSL certificates +docker run --rm --entrypoint="" \ + --volume="$(pwd)/integration_test/dendrite/data":/mnt:rw \ + matrixdotorg/dendrite-monolith:latest \ + /usr/bin/generate-keys \ + -private-key /mnt/matrix_key.pem \ + -tls-cert /mnt/server.crt \ + -tls-key /mnt/server.key + + docker run -d --volume="$(pwd)/integration_test/dendrite/data":/etc/dendrite:rw \ + --name dendrite -p 8008:8008 matrixdotorg/dendrite-monolith:latest -really-enable-open-registration + +sleep 20 \ No newline at end of file diff --git a/scripts/integration-server-synapse.sh b/scripts/integration-server-synapse.sh new file mode 100755 index 000000000..46cbc4f99 --- /dev/null +++ b/scripts/integration-server-synapse.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +chown -R 991:991 integration_test/synapse +docker run -d --name synapse --user 991:991 --volume="$(pwd)/integration_test/synapse/data":/data:rw -p 8008:8008 matrixdotorg/synapse:latest +sleep 20 \ No newline at end of file diff --git a/scripts/integration-start-avd.sh b/scripts/integration-start-avd.sh new file mode 100755 index 000000000..6dd188f6f --- /dev/null +++ b/scripts/integration-start-avd.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +chmod 777 -R /dev/kvm +adb start-server +emulator -avd test -no-audio -no-boot-anim -no-window -accel on -gpu swiftshader_indirect \ No newline at end of file diff --git a/scripts/prepare-android-release.sh b/scripts/prepare-android-release.sh index 8204f7ca1..a2a11dcb1 100755 --- a/scripts/prepare-android-release.sh +++ b/scripts/prepare-android-release.sh @@ -1,5 +1,4 @@ #!/usr/bin/env bash -git apply ./scripts/enable-android-google-services.patch cd android echo $FDROID_KEY | base64 --decode --ignore-garbage > key.jks echo "storePassword=${FDROID_KEY_PASS}" >> key.properties