Skip to content

Commit

Permalink
feat: dev to test (#477)
Browse files Browse the repository at this point in the history
* feat(EWM-264): account balances (#474)

Co-authored-by: Egor Komarov <egor.komarov@bf.rocks>

* refactor: Remove language menu (#476)

* app-deploy.yaml on push branches test

* Add branch-workflow.yaml

* melos.yaml add deploy_ios, deploy_android, deploy_fad commands

* Update README.md

* Rename to deploy_fad_ios and deploy_fad_android

* Move get SENTRY_DSN to build.sh

* build.sh deploy to store only from main

* app-deploy.yaml store job check branch to deploy to stores

* Hide language item from menu in profile

* Update build flow

* Update Readme Deploy from local machine section

* Fix android-deploy orders

* fix: Ewm 250 (#478)

* EWM-250. Fix add seed

* PopScope replace onPopInvoked -> onPopInvokedWithResult + addPostFrameCallback

* Remove todo

* add check didPop in onPopInvokedWithResult

* Fix create new seed from profile

* Fix analyzer issue

* SeedPhraseModel

---------

Co-authored-by: Egor Komarov <Odrin@users.noreply.github.com>
Co-authored-by: Egor Komarov <egor.komarov@bf.rocks>
  • Loading branch information
3 people authored Sep 8, 2024
1 parent c2563c0 commit ef8be13
Show file tree
Hide file tree
Showing 35 changed files with 580 additions and 335 deletions.
50 changes: 21 additions & 29 deletions .github/workflows/app-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Deploy app
on:
push:
branches:
- main
- test
workflow_dispatch:
inputs:
deploy_target:
Expand Down Expand Up @@ -34,6 +34,16 @@ jobs:
with:
fetch-depth: 1

- name: Check deploy target and branch
run: |
if [[ "${{ github.ref }}" != "refs/heads/main" &&
( "${{ inputs.deploy_target }}" == "store" ||
"${{ inputs.deploy_target }}" == "ios_store" ||
"${{ inputs.deploy_target }}" == "android_store" ) ]]; then
echo "🚫 Error: Deployment to store, ios_store, or android_store is only allowed from the 'main' branch."
exit 1
fi
- name: Set output for flutter_version
id: set_output
run: |
Expand Down Expand Up @@ -133,25 +143,16 @@ jobs:
- name: Generate dart code
run: melos codegen --no-select

# Read Sentry DSN
- name: Read Sentry DSN
id: read_sentry_dsn
run: |
if [ -f ./secrets/sentry-dsn.txt ]; then
SENTRY_DSN=$(cat ./secrets/sentry-dsn.txt)
echo "SENTRY_DSN=$SENTRY_DSN" >> $GITHUB_ENV
echo "::add-mask::$SENTRY_DSN"
fi
- name: Display Build Number
run:
echo "Number is = ${{ needs.setup.outputs.build_number }}"

- name: Build and deploy
run: |
bash scripts/build.sh ${{ env.ios_target }} ${{ needs.setup.outputs.build_number }}
env:
SENTRY_DSN: ${{ env.SENTRY_DSN }}
bash scripts/build.sh \
--deploy-target ${{ env.ios_target }} \
--build-number ${{ needs.setup.outputs.build_number }} \
--upload true
- name: Clean Xcode Derived Data
run: |
Expand All @@ -175,17 +176,17 @@ jobs:
# Android deploy
android-deploy:
needs: setup
runs-on: ubuntu-latest

if: ${{ inputs.deploy_target == 'fad' || inputs.deploy_target == 'store' || inputs.deploy_target == 'android_fad' || inputs.deploy_target == 'android_store' }}

runs-on: ubuntu-latest
env:
SECRET_PASSPHRASE: ${{ secrets.SECRET_PASSPHRASE }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}

steps:
- name: Reinstall existing fastlane plugin
run: |
run: |
sudo gem uninstall fastlane-plugin-firebase_app_distribution
gem install fastlane-plugin-firebase_app_distribution --user-install
Expand Down Expand Up @@ -231,25 +232,16 @@ jobs:
- name: Generate dart code
run: melos codegen --no-select

# Read Sentry DSN
- name: Read Sentry DSN
id: read_sentry_dsn
run: |
if [ -f ./secrets/sentry-dsn.txt ]; then
SENTRY_DSN=$(cat ./secrets/sentry-dsn.txt)
echo "SENTRY_DSN=$SENTRY_DSN" >> $GITHUB_ENV
echo "::add-mask::$SENTRY_DSN"
fi
- name: Display Build Number
run:
echo "Number is = ${{ needs.setup.outputs.build_number }}"

- name: Build and deploy
run: |
bash scripts/build.sh ${{ env.android_target }} ${{ needs.setup.outputs.build_number }}
env:
SENTRY_DSN: ${{ env.SENTRY_DSN }}
bash scripts/build.sh \
--deploy-target ${{ env.android_target }} \
--build-number ${{ needs.setup.outputs.build_number }} \
--upload true
- name: Remove Flutter
run: |
Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/branch-workflow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Check branch

on:
pull_request:
branches:
- main

jobs:
prevent-dev-merge:
runs-on: ubuntu-latest
steps:
- name: Check source branch
run: |
if [[ "${{ github.event.pull_request.head.ref }}" == "dev" ]]; then
echo "Merging from 'dev' to 'main' is not allowed."
exit 1
else
echo "Merge allowed."
fi
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -360,12 +360,45 @@ All deployment scripts are gathering changelog from git commits and adding it to

### Deploy using GitHub Actions

We already have a workflow for deploying main app to Firebase App Distribution, TestFlight and Google Play closed testing. It's called `app=deploy` and it's triggered by pushing to the main branch or manually from any branch. You can choose the desired deployment when triggering the workflow manually. It will deploy to FAD when its triggered by pushing to the main branch.
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.

```sh
# To deploy to Firebase App Distribution just run the following command:
$ melos build:deploy_fad
Expand Down
3 changes: 2 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
package="com.broxus.sparx.app">
<application
android:label="${appName}"
android:icon="@mipmap/ic_launcher">
android:icon="@mipmap/ic_launcher"
android:enableOnBackInvokedCallback="true">
<activity
android:name=".MainActivity"
android:exported="true"
Expand Down
59 changes: 29 additions & 30 deletions lib/app/router/routs/add_seed/add_seed.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'dart:convert';

import 'package:app/app/router/router.dart';
import 'package:app/data/models/seed/seed_phrase_model.dart';
import 'package:app/feature/add_seed/add_existing_wallet/view/add_existing_wallet_page.dart';
import 'package:app/feature/add_seed/add_seed_enable_biometry/view/add_seed_enable_biometry_page.dart';
import 'package:app/feature/add_seed/create_password/screens/create_seed_password/create_seed_password_screen.dart';
Expand All @@ -24,6 +23,8 @@ const enterSeedNameNamePathParam = 'nameParam';
/// Route that allows to create a seed phrase without entering name.
/// This route may be used in onboarding or profile section, depends
/// on [passwordRoute].
// TODO(knightforce): check is used
@Deprecated('Use v2 version')
GoRoute createSeedNoNamedRoute(GoRoute passwordRoute) {
return GoRoute(
Expand All @@ -33,10 +34,9 @@ GoRoute createSeedNoNamedRoute(GoRoute passwordRoute) {
GoRoute(
path: AppRoute.checkSeed.path,
builder: (_, state) => CheckSeedPhrasePage(
phrase: (jsonDecode(
state.uri.queryParameters[addSeedPhraseQueryParam]!,
) as List<dynamic>)
.cast<String>(),
seed: SeedPhraseModel(
state.uri.queryParameters[addSeedPhraseQueryParam],
),
),
routes: [
passwordRoute,
Expand Down Expand Up @@ -93,7 +93,9 @@ GoRoute get createOnboardingSeedPasswordRoute {
path: AppRoute.createSeedPassword.path,
builder: (_, GoRouterState state) {
return CreateSeedPasswordScreen(
phrase: state.uri.queryParameters[addSeedPhraseQueryParam],
phrase: SeedPhraseModel(
state.uri.queryParameters[addSeedPhraseQueryParam],
),
);
},
routes: [
Expand All @@ -116,10 +118,9 @@ GoRoute get createSeedNoNamedProfileRoute {
GoRoute(
path: AppRoute.createSeedPassword.path,
builder: (_, state) => CreateSeedPasswordProfilePage(
phrase: (jsonDecode(
state.uri.queryParameters[addSeedPhraseQueryParam]!,
) as List<dynamic>)
.cast<String>(),
seedPhrase: SeedPhraseModel(
state.uri.queryParameters[addSeedPhraseQueryParam],
),
name: state.pathParameters[enterSeedNameNamePathParam],
),
),
Expand All @@ -132,10 +133,9 @@ GoRoute get enterSeedNoNamedProfileRoute {
GoRoute(
path: AppRoute.createSeedPassword.path,
builder: (_, GoRouterState state) => CreateSeedPasswordProfilePage(
phrase: (jsonDecode(
state.uri.queryParameters[addSeedPhraseQueryParam]!,
) as List<dynamic>)
.cast<String>(),
seedPhrase: SeedPhraseModel(
state.uri.queryParameters[addSeedPhraseQueryParam],
),
name: state.pathParameters[enterSeedNameNamePathParam],
),
),
Expand All @@ -149,10 +149,9 @@ GoRoute get createSeedNamedProfileRoute {
final passwordRoute = GoRoute(
path: AppRoute.createSeedPassword.path,
builder: (_, GoRouterState state) => CreateSeedPasswordProfilePage(
phrase: (jsonDecode(
state.uri.queryParameters[addSeedPhraseQueryParam]!,
) as List<dynamic>)
.cast<String>(),
seedPhrase: SeedPhraseModel(
state.uri.queryParameters[addSeedPhraseQueryParam],
),
name: state.pathParameters[enterSeedNameNamePathParam],
),
);
Expand All @@ -164,10 +163,9 @@ GoRoute get createSeedNamedProfileRoute {
GoRoute(
path: AppRoute.checkSeed.path,
builder: (_, state) => CheckSeedPhrasePage(
phrase: (jsonDecode(
state.uri.queryParameters[addSeedPhraseQueryParam]!,
) as List<dynamic>)
.cast<String>(),
seed: SeedPhraseModel(
state.uri.queryParameters[addSeedPhraseQueryParam],
),
),
routes: [
passwordRoute,
Expand All @@ -188,13 +186,14 @@ GoRoute get enterSeedNamedProfileRoute {
routes: [
GoRoute(
path: AppRoute.createSeedPassword.path,
builder: (_, GoRouterState state) => CreateSeedPasswordProfilePage(
phrase: (jsonDecode(
state.uri.queryParameters[addSeedPhraseQueryParam]!,
) as List<dynamic>)
.cast<String>(),
name: state.pathParameters[enterSeedNameNamePathParam],
),
builder: (_, GoRouterState state) {
return CreateSeedPasswordProfilePage(
seedPhrase: SeedPhraseModel(
state.uri.queryParameters[addSeedPhraseQueryParam],
),
name: state.pathParameters[enterSeedNameNamePathParam],
);
},
),
],
);
Expand Down
22 changes: 22 additions & 0 deletions lib/data/models/seed/seed_phrase_model.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class SeedPhraseModel {
SeedPhraseModel(String? phrase) : phrase = phrase ?? '' {
_words = null;
}

SeedPhraseModel.fromWords(List<String> list) {
phrase = list.join(' ');
_words = list;
}

SeedPhraseModel.empty() : this(null);

late final String phrase;
late final List<String> words = _words ?? phrase.split(' ');

late final wordsCount = words.length;

late final isEmpty = phrase.isEmpty;
late final isNotEmpty = !isEmpty;

late final List<String>? _words;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:app/app/service/network_connection/network_connection_service.dart';
import 'package:app/app/service/service.dart';
import 'package:app/data/models/seed/seed_phrase_model.dart';
import 'package:app/di/di.dart';
import 'package:app/feature/add_seed/create_password/model/password_status.dart';
import 'package:app/utils/mixins/connection_mixin.dart';
Expand All @@ -19,8 +20,8 @@ const _minPasswordLength = 8;
class CreateSeedPasswordCubit extends Cubit<CreateSeedPasswordState>
with ConnectionMixin {
CreateSeedPasswordCubit({
required this.phrase,
required this.completeCallback,
required this.seedPhrase,
this.setCurrentKey = false,
this.name,
}) : super(CreateSeedPasswordState.initial()) {
Expand All @@ -32,7 +33,7 @@ class CreateSeedPasswordCubit extends Cubit<CreateSeedPasswordState>
final VoidCallback completeCallback;

/// Phrase that must be used to create seed
final List<String> phrase;
final SeedPhraseModel seedPhrase;

/// Name of seed phrase if provided
final String? name;
Expand Down Expand Up @@ -67,7 +68,7 @@ class CreateSeedPasswordCubit extends Cubit<CreateSeedPasswordState>
}

Future<void> nextAction() async {
if (!await checkConnection()) {
if (seedPhrase.isEmpty || !await checkConnection()) {
return;
}

Expand All @@ -76,7 +77,7 @@ class CreateSeedPasswordCubit extends Cubit<CreateSeedPasswordState>
final currentKeyService = inject<CurrentKeyService>();
try {
final publicKey = await nekoton.addSeed(
phrase: phrase,
phrase: seedPhrase.words,
password: passwordController.text,
name: name,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:app/data/models/seed/seed_phrase_model.dart';
import 'package:app/feature/add_seed/create_password/model/password_status.dart';
import 'package:app/feature/add_seed/create_password/screens/create_seed_password/create_seed_password_screen_wm.dart';
import 'package:app/feature/add_seed/create_password/view/create_seed_password_view.dart';
Expand All @@ -15,7 +16,7 @@ class CreateSeedPasswordScreen
CreateSeedPasswordScreen({
Key? key,
WidgetModelFactory<CreateSeedPasswordScreenWidgetModel>? wmFactory,
String? phrase,
SeedPhraseModel? phrase,
}) : super(
wmFactory ??
(context) => defaultCreateSeedPasswordScreenWidgetModelFactory(
Expand Down
Loading

0 comments on commit ef8be13

Please sign in to comment.