Skip to content

Latest commit

Β 

History

History
561 lines (376 loc) Β· 17.4 KB

README.md

File metadata and controls

561 lines (376 loc) Β· 17.4 KB

App

style: very good analysis app

EVER Wallet Flutter app


Getting Started πŸš€

Install melos:

$ dart pub global activate melos

Using melos makes it very easy to work with the project, so enjoy.

Boostrap packages recursively:

$ melos bs

Codegen 🦾

This thing will run all code generators for all packages:

$ melos run codegen

Codegen: localization 🌐

This thing will run localization code generator for all packages:

$ melos run codegen:locale

Code format checking and analyser 🦠

$ melos run check-format
$ melos run analyze

Clean up 🧹

To clean up all packages just run:

$ melos clean

Running Tests πŸ§ͺ

To run all unit and widget tests use the following command:

$ melos test

Also you can run dart, flutter and integration tests separately:

$ melos test:dart
$ melos test:flutter
$ melos test:integration

Pre-commit preparation 🦠πŸ§ͺ🀏

To run code format check, analyzer and all tests use the following command:

$ melos check-all

Build storybook πŸ“š

To build storybook:

$ melos build:storybook

Flavours πŸ‘ƒ

This project contains 3 flavors:

  • development
  • staging
  • production

To run the desired flavor either use the launch configuration in VSCode/Android Studio or use the following commands:

# Development
$ flutter run --flavor development --target lib/main_development.dart

# Staging
$ flutter run --flavor staging --target lib/main_staging.dart

# Production
$ flutter run --flavor production --target lib/main_production.dart

*App works on iOS and Android. It should run on macOS, Linux and Windows, but we have no tests nor UI kit for desktop platforms.

Versioning the app πŸ”’

App version #️⃣

App version is defined in pubspec.yaml file. To bump the version use the following command:

# For development releases:
$ melos version -a --yes --prerelease

# For production releases:
$ melos version -a --yes --graduate

You can use version workflow in GitHub actions to bump the version automatically. This workflow will create a new branch and PR (because push to main is prohibited) with the new version. Don't forget to merge the PR to main! Note: you should use conventional commits to make it work.

App build number #️⃣

App build number is defined in pubspec.yaml file. However, there is a tools/get_build_number.dart tool that generates the build number by incrementing it transactionally in Firebase Realtime Database. So, you don't need to worry about the build number, it's always unique and increases monotonously. Every time you build the app, the build number will be incremented.

Secrets πŸ”’

Local .secrets file πŸ”‘

Application secret data is located in the project root in the secrets folder.

β”œβ”€β”€ lib
β”œβ”€β”€ ...
β”œβ”€β”€ secrets/
β”‚   β”œβ”€β”€ secrets.tar.gpg
β”‚   β”œβ”€β”€ .secrets.example
β”‚   └── .secrets

secrets.tar.gpg encrypted archive for secure storage of confidential data

.secrets.example example/template for your own .secrets file

.secrets file for your own secrets. Not in the repository, you need to create it yourself. To create just copy secrets/.secrets.example to secrets/.secrets and fill it with your secrets.

This file contains secret keys used when running commands.

Example contents of a .secrets file:

export SECRET_PASSPHRASE="abc"
export MATCH_PASSWORD="qwerty"
export FASTLANE_USER="example@gmail.com"
export FASTLANE_PASSWORD="pass"

Warning: don't commit .secrets file!

FASTLANE_USER and FASTLANE_PASSWORD are YOUR OWN credentials, that will be used only to renew iOS certificates and provisioning profiles.

FASTLANE_USER should be set to your Apple Developer login. FASTLANE_PASSWORD should be your Apple Developer password.

Other secrets you can get from your teammates:

SECRET_PASSPHRASE: passphrase the GPG tarball (with secrets) MATCH_PASSWORD: passphrase for iOS provisioning profiles and certificates. Used for Fastlane's match command.

Github Secrets πŸ”‘

BOT_ACCESS_TOKEN: Personal access token (PAT) used to fetch the repository. We should use PAT and not default GITHUB_TOKEN because "When you use the repository's GITHUB_TOKEN to perform tasks, events triggered by the GITHUB_TOKEN, with the exception of workflow_dispatch and repository_dispatch, will not create a new workflow run". We want to trigger a workflow from the workflow (to run tests), so we need to use PAT. This thing is used in version workflow.

SECRET_PASSPHRASE and MATCH_PASSWORD is the same as in local .secrets file.

Warning: SECRET_PASSPHRASE needs to be located in CI/CD (when using Github Actions, located in the Secrets of the repository) to decrypt the gpg file.

File with secrets: secrets/secrets.tar.gpg πŸ”’πŸ“

This file contains all secrets and is encrypted with GPG. To decrypt it, run the following command:

$ melos decrypt-secrets

This command will place the secrets files in the necessary directories: the /secrets, /android and /ios folders.

When performing a build via Github Actions, secrets are temporarily located in the required folders. After building the application, scripts/clean.sh is run to remove secrets.

Create secrets

To create a gpg file with secrets, create a directory structure somewhere:

somefolder/
β”œβ”€β”€ android/
β”‚   β”œβ”€β”€ your_keystore_name.keystore
β”‚   β”œβ”€β”€ fastlane/
β”‚   β”‚   β”œβ”€β”€ GooglePlayServiceAccount.json
β”‚   β”‚   └── key.properties
β”œβ”€β”€ fastlane/
β”‚   β”œβ”€β”€ FirebaseADKey.json
β”‚   └── FirebaseAPIKey.json
β”œβ”€β”€ ios/
β”‚   β”œβ”€β”€ fastlane/
β”‚   β”‚   └── YourDeveloperAppleAuthKey.p8
β”œβ”€β”€ secrets/
β”‚   β”œβ”€β”€ ios-provisioning-key
β”‚   β”œβ”€β”€ ios-provisioning-key.pub
β”‚   └── sentry-dsn.txt

/android/your_keystore_name.keystore Keystore file in jks format. Used to sign an APK file

/android/fastlane/GooglePlayServiceAccount.json File with Google service account credentials for interacting with the Google API and Google Play Developer API.

/android/key.properties File with data for signing APK files.

/fastlane/FirebaseADKey.json Needed to interact with the Firebase API. Contains data for scripts to interact with Firebase services.

/fastlane/FirebaseAPIKey.json Used to update the build number in Firebase Realtime Database.

/ios/fastlane/YourDeveloperAppleAuthKey.p8 Required to work with the App Store Connect API.

/secrets/ios-provisioning-key Provisioning Profile file

/secrets/ios-provisioning-key.pub Apple public key to run match_assure.

/secrets/sentry-dsn.txt Text file with dns for working with sentry.

After that, in the directory with the created directories, run the command:

$ tar -cf secrets.tar *
$ gpg --symmetric --cipher-algo AES256 secrets.tar

Update secrets

To update secrets, decrypt the gpg file.

You will be asked to enter the passphrase. This passphrase should be SECRET_PASSPHRASE from .secrets file:

$ gpg --output secrets.tar --decrypt secrets.tar.gpg

Unpack the archive:

tar -xf secrets.tar

Update the necessary files and package them again:

$ tar c * > secrets.tar
$ gpg --symmetric --cipher-algo AES256 secrets.tar

Resulting secrets.tar.gpg file should be placed in secrets directory in the root of the project.

Make sure that the old gpg file does not end up in the archive.

Warning: When archiving the contents in secrets folder, do not forget to exclude unnecessary files, such old gpg file and .secrets with your personal data

iOS certificates and provisioning profiles πŸ“œ

For iOS we use match to manage certificates and provisioning profiles. It's configured in ios/fastlane/Matchfile.
There is no need to manually create certificates and profiles; match will do everything for you and save it.

To renew certificates and provisioning profiles after adding new devices to the Apple Developer Account, run the following command:

$ melos build:ios_match_new_devices

If you configuring a new machine, you should run the following command to install certificates and provisioning profiles:

$ melos build:ios_match_assure

The password for executing match commands is taken from the .secrets/MATCH_PASSWORD file.

Warning: If there are problems with the certificates or they are out of date, use a set of commands to create new certificates:

fastlane match nuke development
fastlane match nuke distribution
melos run build:ios_match_assure

This will not cause problems even if other application certificates are affected.

Possible problems

If the 6-digit code is not entered

When running the melos run build:ios_match_assure command through the terminal or another fastlane command that requires entering a 6-digit apple verification code, the code may be ignored by the terminal. Instead of entering code, a line break occurs.

To work around this issue, in the project root or another location, create a text file fastlane_session.txt (the name can be anything).

Manually or via command:

$ touch fastlane_session.txt 

Create a token to work with the fastlane session:

$ fastlane spaceauth -u YOUE_APPLE_ID  

Copy the token from the console and paste it into the fastlane_session.txt file. Remove extra characters such as \n

Example contents of the fastlane_session.txt file:

---
- !ruby/object:HTTP::Cookie
  name: abcdefg
  value: ABCDEFG111111cf12345a12
  for_domain: true
  path: "/"
  secure: true
  httponly: true
  expires:
  max_age:
  created_at: 2024-06-07 23:51:26.367930000 +07:00
  accessed_at: 2024-06-07 23:51:26.372267000 +07:00
- !ruby/object:HTTP::Cookie
  name: ABCDE
  ...

In the terminal, run the command to set the FASTLANE_SESSION environment variable with the value from the fastlane_session.txt file.

$ export FASTLANE_SESSION=$(cat fastlane_session.txt)

After this, fastlane commands will no longer require the 6-digit Apple verification code.

It takes a long time to clone the repository when running melos build:ios_match_new_devices

If cloning the repository takes a long time when running the command melos build:ios_match_new_devices, then possible reasons:

  1. Insufficient rights. Contact the administrator.
  2. You need an ssh key.

Deploy πŸš€

We prefer to deploy the app using GitHub Actions, but you can also deploy it from your local machine.

All deployment scripts are gathering changelog from git commits and adding it to the release notes. Note: google play limits the release notes, so don't be surprised that it is simplified and shortened.

Deploy using GitHub Actions

We already have a workflow for deploying main app to Firebase App Distribution, TestFlight and Google Play. It's called Deploy app.

Deployment to fab is triggered when the test branch is updated, or when manually launched from any branch except main. Deployment to store is triggered when manually launched from the main branch.

Deployment to FAD is triggered when the dev branch is updated.

Manually running the deploy melos run deploy_fad from any branch except main.
Running melos run deploy_ios deploys iOS to FAD.
Running melos run deploy_android deploys Android to FAD.

Deployment to store is triggered when manually launched from the main branch.

Available options for deploying in workflow:

  • ios_fad - launches iOS deploy to FAD.
  • ios_store - launches iOS deploy to Test Flight.
  • android_fad - launches Android deploy to FAD.
  • android_store - launches Android deploy to Google Play.
  • fad - launches iOS and Android deploy to FAD.
  • store - launches iOS and Android deploy to Test Flight and Google Play.

We also have a workflow for deploying storybook to GitHub Pages. It's called storybook-gh-pages-deploy and it's triggered by pushing to the main branch or manually from any branch.

Deploy from local machine

For deployment from a local machine, melos is used.

  • build_android_store - build Android aab from any branch
  • deploy_fad_ios - build and send iOS ipa from any branch
  • deploy_fad_android - build and send Android apk from any branch
  • deploy_fad - build and send Android apk and iOS ipa from any branch
  • deploy_store - build and send Android aab and iOS ipa from the main branch

The melos deploy_store* commands only work from the main branch, so as not to accidentally upload unnecessary code to production.

The melos build_* commands work from any branch - in case of manual build.
Unlike deploy_store* commands, randomly running build_* will only build aab and/or ipa locally and will not push anything extra or untested to the store.

# To deploy to Firebase App Distribution just run the following command:
$ melos build:deploy_fad

# To deploy to TestFlight and Google Play closed testing just run the following command:
$ melos build:deploy_store

Each of these commands will increment the build number before building the app.

Warning: To use Sentry, don't forget to pass the dsn via dart-define.

Example:

melos build:deploy_fad -- --dart-define=SENTRY_DSN="your_dsn"

Coverage πŸ“Š

To view the generated coverage report you can use lcov.

# Generate Coverage Report
$ genhtml coverage/lcov.info -o coverage/

# Open Coverage Report
$ open coverage/index.html

Logging ( Ν‘β›β€―ΝœΚ– ͑❛)✌

The app can be built with several flavors. Each of them determines the logging level. The defining map is in logs/logs.dart.

We also have logs from nekoton, and level transformation matrix is in packages/nekoton_repository/lib/src/nekoton_repository.dart.

Console colors are defined in fancy_logger package.

Crash analytics

Sentry is used to intercept global errors and crash analytics.

Sentry does not run in the development build type.

Dsn is passed through dart-define using the SENTRY_DSN environment variable.

Examples:

melos build:deploy_fad -- --dart-define=SENTRY_DSN="your_dsn"
flutter run --dart-define=SENTRY_DSN="your_sentry_dsn_value"

Working with Translations 🌐

This project relies on flutter_localizations and follows the official internationalization guide for Flutter. However, we use easy_localization package to simplify the internationalization process.

Adding Strings

  1. To add a new localizable string, open the en.json file at assets/translations/en.json.
{
    "confirm": "Confirm"
}
  1. Then add a new key/value
{
    "confirm": "Confirm",
    "continueWord": "Continue"
}
  1. Use the new string
import 'package:app/generated/generated.dart';

@override
Widget build(BuildContext context) {
  return Text(LocaleKeys.continueWord.tr());
}

Adding Supported Locales

Update the CFBundleLocalizations array in the Info.plist at ios/Runner/Info.plist to include the new locale.

    ...

    <key>CFBundleLocalizations</key>
	<array>
		<string>en</string>
		<string>es</string>
	</array>

    ...

Adding Translations

  1. For each supported locale, add a new JSON file in assets/translations.
β”œβ”€β”€ assets
β”‚   β”œβ”€β”€ translations
β”‚   β”‚   β”œβ”€β”€ en.json
β”‚   β”‚   └── es.json
  1. Add the translated strings to each .json file:

es.json

{
    "confirm": "Confirmar",
    "continueWord": "Continuar"
}
  1. Provide flag icon asset

Put new vector icon to assets/images/lang_icons/spanish.svg.

  1. Add the locale to SupportedLocaleCodes enum in lib/app/service/localization/service/supported_locale_codes.dart (yes, don't forget the language string).
    ...
    es(LocaleKeys.langSpanish),
    ...
  1. Provide flag icon asset and iconPath in SupportedLocaleCodes enum
    ...
    SupportedLocaleCodes.ko => Assets.images.langIcons.spainsh.path,
    ...