diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000000..bdcd36a4cc --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,109 @@ +name: Lint + +on: [pull_request] + +defaults: + run: + shell: bash +jobs: + lint: + runs-on: [Linux, amd64, android] + container: + image: ghcr.io/cake-tech/cake_wallet:debian13-flutter3.41.9-ndkr28-go1.24.1-ruststablenightly + env: + STORE_PASS: test@cake_wallet + KEY_PASS: test@cake_wallet + MONEROC_CACHE_DIR_ROOT: /opt/generic_cache + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + ANDROID_AVD_HOME: /root/.android/avd + volumes: + - /opt/cw_cache_android/root/.cache:/root/.cache + - /opt/cw_cache_android/root/.android/avd/:/root/.android/avd + - /opt/cw_cache_android/root/.ccache:/root/.ccache + - /opt/cw_cache_android/root/.pub-cache/:/root/.pub-cache + - /opt/cw_cache_android/root/.gradle/:/root/.gradle + - /opt/cw_cache_android/root/.android/:/root/.android + - /opt/cw_cache_android/root/go/pkg:/root/go/pkg + - /opt/cw_cache_android/opt/generic_cache:/opt/generic_cache + - /dev/kvm:/dev/kvm + - /var/run/docker.sock:/var/run/docker.sock + strategy: + matrix: + api-level: [ 29 ] + + steps: + - name: configure git + run: | + git config --global --add safe.directory '*' + git config --global user.email "ci@cakewallet.com" + git config --global user.name "CakeWallet CI" + + - name: Fetch prebuilt Torch + run: | + set -x -e + pushd scripts + wget https://github.com/MrCyjaneK/torch_dart/releases/download/v1.0.17/torch_dart-v1.0.17.tar.gz -O torch_dart.tar.gz + rm -rf torch_dart + mkdir torch_dart + pushd torch_dart + tar -xzf ../torch_dart.tar.gz + popd + rm ./torch_dart.tar.gz + popd + + - name: Fetch prebuilt Reown + run: | + set -x -e + pushd scripts + # cleaning + rm -rf reown_flutter + rm -f reown_flutter.tar.gz + + wget https://github.com/cake-tech/reown_flutter/releases/download/v0.0.4/reown_flutter-v0.0.4.tar.gz -O reown_flutter.tar.gz + mkdir reown_flutter + pushd reown_flutter + tar -xzf ../reown_flutter.tar.gz + popd + rm ./reown_flutter.tar.gz + popd + + - name: Build Bitbox Flutter + run: | + set -x -e + pushd scripts + ./build_bitbox_flutter.sh + popd + + - name: Prepare Zcash + run: | + set -x -e + scripts/prepare_zcash.sh + + - name: Execute Build and Setup Commands + run: | + pushd scripts/android + source ./app_env.sh cakewallet + export GITHUB_HUH=yeah + ./app_config.sh + popd + + - name: Install Flutter dependencies + run: | + flutter pub get + + - name: Build generated code + run: | + flutter --version + flutter clean + rm -rf .dart_tool + rm pubspec.lock + flutter pub get + ./model_generator.sh async + + - name: Generate localization + run: | + dart run tool/generate_localization.dart + + - name: Lint + run: | + ABORT_ON_CHANGE=yeah scripts/lint.sh \ No newline at end of file diff --git a/.github/workflows/no_http_imports.yaml b/.github/workflows/no_http_imports.yaml index dad6821ac1..0bfca1d063 100644 --- a/.github/workflows/no_http_imports.yaml +++ b/.github/workflows/no_http_imports.yaml @@ -11,11 +11,10 @@ jobs: - name: Check for http package usage if: github.event_name == 'pull_request' run: | - GIT_GREP_OUT="$(git grep package:http | (grep .dart: || test $? = 1) | (grep -v proxy_wrapper.dart || test $? = 1) | (grep -v very_insecure_http_do_not_use || test $? = 1) || true)" + GIT_GREP_OUT="$(git grep package:http | (grep .dart: || test $? = 1) | (grep -v proxy_wrapper.dart || test $? = 1) | (grep -v very_insecure_http_do_not_use || test $? = 1) | (grep -v '^cw_custom_lints/' || test $? = 1) || true)" [[ "x$GIT_GREP_OUT" == "x" ]] && exit 0 echo "$GIT_GREP_OUT" echo "There are .dart files which use http imports" echo "Using http package breaks proxy integration" echo "Please use ProxyWrapper.getHttpClient() from package:cw_core/utils/proxy_wrapper.dart" exit 1 - \ No newline at end of file diff --git a/.github/workflows/no_print_in_dart.yaml b/.github/workflows/no_print_in_dart.yaml index 507793bd81..69885c9b5c 100644 --- a/.github/workflows/no_print_in_dart.yaml +++ b/.github/workflows/no_print_in_dart.yaml @@ -11,7 +11,7 @@ jobs: - name: Check for print() statements in dart code (use printV() instead) if: github.event_name == 'pull_request' run: | - GIT_GREP_OUT="$(git grep ' print(' | (grep .dart: || test $? = 1) | (grep -v print_verbose.dart || test $? = 1) | (grep -v print_verbose_dummy.dart || test $? = 1) || true)" + GIT_GREP_OUT="$(git grep ' print(' | (grep .dart: || test $? = 1) | (grep -v print_verbose.dart || test $? = 1) | (grep -v print_verbose_dummy.dart || test $? = 1) | (grep -v '^tool/' || test $? = 1) | (grep -v '^cw_custom_lints/' || test $? = 1) || true)" [[ "x$GIT_GREP_OUT" == "x" ]] && exit 0 echo "$GIT_GREP_OUT" echo "There are .dart files which use print() statements" diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml index 8cb4424476..879ea247fd 100644 --- a/.github/workflows/pr_test_build_linux.yml +++ b/.github/workflows/pr_test_build_linux.yml @@ -179,9 +179,6 @@ jobs: with: path: ${{ github.workspace }}/build/linux/x64/release/cakewallet_linux.zip name: cakewallet_linux - - name: Verify lint - run: | - exec env ABORT_ON_CHANGE=true ./scripts/lint.sh - name: Prepare virtual desktop if: ${{ contains(env.message, 'run tests') }} diff --git a/analysis_options.yaml b/analysis_options.yaml index 98dfe6ae42..70e87faf31 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,4 +1,3 @@ -include: package:lints/recommended.yaml analyzer: @@ -9,6 +8,7 @@ analyzer: cw_core/lib/**.g.dart, cw_haven/lib/**.g.dart, cw_monero/lib/**.g.dart, + cw_mweb/lib/**.g.dart, lib/generated/*.dart, cw_monero/ios/External/**, cw_shared_external/**, @@ -18,65 +18,124 @@ analyzer: strict-casts: true strict-raw-types: true +plugins: + cw_custom_lints: + path: cw_custom_lints + linter: rules: - cancel_subscriptions - always_declare_return_types - prefer_final_fields + - always_put_control_body_on_new_line + - always_put_required_named_parameters_first + - always_use_package_imports + - annotate_overrides + - annotate_redeclares + - avoid_empty_else + - avoid_function_literals_in_foreach_calls + - avoid_init_to_null + - avoid_multiple_declarations_per_line + - avoid_positional_boolean_parameters + - avoid_relative_lib_imports + - avoid_renaming_method_parameters + - avoid_return_types_on_setters + - avoid_returning_null_for_void + - avoid_slow_async_io + - avoid_types_as_parameter_names + - avoid_types_on_closure_parameters + - avoid_unnecessary_containers + - avoid_unused_constructor_parameters + - avoid_void_async + - await_only_futures + - camel_case_extensions + - camel_case_types + - cast_nullable_to_non_nullable + - collection_methods_unrelated_type + - constant_identifier_names + - curly_braces_in_flow_control_structures + - directives_ordering + - empty_constructor_bodies + - empty_statements + - eol_at_end_of_file + - flutter_style_todos + - hash_and_equals + - implicit_reopen + - library_private_types_in_public_api + - non_constant_identifier_names + - only_throw_errors + - package_names + - prefer_asserts_with_message + - prefer_conditional_assignment + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_constructors_over_static_methods + - prefer_contains + - prefer_double_quotes + - prefer_expression_function_bodies + - prefer_final_in_for_each + - prefer_function_declarations_over_variables + - prefer_if_null_operators + - prefer_initializing_formals + - prefer_inlined_adds + - prefer_int_literals + - prefer_is_empty + - prefer_is_not_empty + - prefer_is_not_operator + - prefer_iterable_wheretype + - prefer_null_aware_operators + - prefer_spread_collections + - prefer_typing_uninitialized_variables + - recursive_getters + - require_trailing_commas + - sized_box_for_whitespace + - sort_child_properties_last + - sort_constructors_first + - sort_unnamed_constructors_first + - test_types_in_equals + - unawaited_futures + - unnecessary_async + - unnecessary_await_in_return + - unnecessary_const + - unnecessary_constructor_name + - unnecessary_lambdas + - unnecessary_late + - unnecessary_new + - unnecessary_null_aware_assignments + - unnecessary_null_checks + - unnecessary_null_in_if_null_operators + - unnecessary_nullable_for_final_variable_declarations + - unnecessary_overrides + - unnecessary_parenthesis + - unnecessary_string_escapes + - unnecessary_string_interpolations + - unnecessary_this + - unnecessary_to_list_in_spreads + - unnecessary_underscores + - unreachable_from_main + - unrelated_type_equality_checks + - use_build_context_synchronously + - use_colored_box + - use_full_hex_values_for_flutter_colors + - use_function_type_syntax_for_parameters + - use_is_even_rather_than_modulo + - use_named_constants + - use_null_aware_elements + - use_raw_strings + - use_rethrow_when_possible + - use_string_buffers + - use_super_parameters + - use_truncating_division + - valid_regexps + - void_checks + - prefer_final_locals + - prefer_interpolation_to_compose_strings + - null_closures + - no_wildcard_variable_uses -formatter: - page_width: 100 - -# analyzer: -# strong-mode: -# implicit-casts: false -# implicit-dynamic: false -# exclude: [build/**, lib/generated/*.dart, lib/**.g.dart, cw_monero/ios/External/**, cw_shared_external/**, shared_external/**] -# linter: -# rules: -# - always_declare_return_types -# - annotate_overrides -# - avoid_empty_else -# - avoid_init_to_null -# - avoid_return_types_on_setters -# - await_only_futures -# - camel_case_types -# - cancel_subscriptions -# - close_sinks -# - comment_references -# - constant_identifier_names -# - control_flow_in_finally -# - empty_catches -# - empty_constructor_bodies -# - empty_statements -# - hash_and_equals -# - invariant_booleans -# - iterable_contains_unrelated_type -# - library_names -# - library_prefixes -# - list_remove_unrelated_type -# - literal_only_boolean_expressions -# - non_constant_identifier_names -# - one_member_abstracts -# - only_throw_errors -# - overridden_fields -# - package_api_docs -# - package_names -# - package_prefixed_library_names -# - parameter_assignments -# - prefer_final_fields -# - prefer_final_locals -# - prefer_is_not_empty -# - slash_for_doc_comments -# - sort_constructors_first -# - sort_unnamed_constructors_first -# - test_types_in_equals -# - throw_in_finally -# - type_init_formals -# - unawaited_futures -# - unnecessary_getters_setters -# - unrelated_type_equality_checks -# - valid_regexps +formatter: + page_width: 100 diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index b0752cf797..98e1ec46cb 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -6,7 +6,7 @@ author: Cake Wallet homepage: https://cakewallet.com environment: - sdk: ">=2.17.5 <3.0.0" + sdk: ">=3.0.0 <4.0.0" flutter: ">=1.20.0" dependencies: diff --git a/cw_bitcoin_cash/analysis_options.yaml b/cw_bitcoin_cash/analysis_options.yaml deleted file mode 100644 index a5744c1cfb..0000000000 --- a/cw_bitcoin_cash/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/cw_custom_lints/lib/http_force_proxy/http_force_proxy_rule.dart b/cw_custom_lints/lib/http_force_proxy/http_force_proxy_rule.dart new file mode 100644 index 0000000000..69d0ea7f7d --- /dev/null +++ b/cw_custom_lints/lib/http_force_proxy/http_force_proxy_rule.dart @@ -0,0 +1,62 @@ +import "package:analyzer/analysis_rule/analysis_rule.dart"; +import "package:analyzer/analysis_rule/rule_context.dart"; +import "package:analyzer/analysis_rule/rule_visitor_registry.dart"; +import "package:analyzer/dart/ast/ast.dart"; +import "package:analyzer/dart/ast/visitor.dart"; +import "package:analyzer/error/error.dart"; + +class HttpForceProxyRule extends AnalysisRule { + HttpForceProxyRule() + : super( + name: "no_http_imports", + description: + "Using the http package breaks proxy integration. Please use ProxyWrapper or alias it as \"very_insecure_http_do_not_use\".", + ); + + static const LintCode code = LintCode( + "no_http_imports", + "Using the http package breaks proxy integration. Please use ProxyWrapper or alias it as \"very_insecure_http_do_not_use\".", + severity: DiagnosticSeverity.WARNING, + ); + + @override + LintCode get diagnosticCode => code; + + @override + void registerNodeProcessors( + RuleVisitorRegistry registry, + RuleContext context, + ) { + final filePath = context.definingUnit.file.path; + + if (filePath.endsWith("proxy_wrapper.dart")) { + return; + } + + registry.addImportDirective(this, _Visitor(this)); + } +} + +class _Visitor extends SimpleAstVisitor { + _Visitor(this.rule); + + final AnalysisRule rule; + + @override + void visitImportDirective(ImportDirective node) { + final uriString = node.uri.stringValue; + if (uriString == null) { + return; + } + + if (uriString.startsWith("package:http")) { + final prefixNode = node.prefix; + if (prefixNode != null && + prefixNode.name == "very_insecure_http_do_not_use") { + return; + } + + rule.reportAtNode(node); + } + } +} diff --git a/cw_custom_lints/lib/main.dart b/cw_custom_lints/lib/main.dart new file mode 100644 index 0000000000..39e07ba30a --- /dev/null +++ b/cw_custom_lints/lib/main.dart @@ -0,0 +1,22 @@ +import "package:analysis_server_plugin/plugin.dart"; +import "package:analysis_server_plugin/registry.dart"; +import "package:cw_custom_lints/http_force_proxy/http_force_proxy_rule.dart"; +import "package:cw_custom_lints/print_verbose/print_verbose_fix.dart"; +import "package:cw_custom_lints/print_verbose/print_verbose_rule.dart"; +import "package:cw_custom_lints/restricted_imports/restricted_imports_rule.dart"; + +final plugin = CwCustomLintsPlugin(); + +class CwCustomLintsPlugin extends Plugin { + @override + String get name => "cw_custom_lints"; + + @override + void register(PluginRegistry registry) { + registry.registerWarningRule(PrintVerboseRule()); + registry.registerWarningRule(RestrictedImportsRule()); + registry.registerWarningRule(HttpForceProxyRule()); + + registry.registerFixForRule(PrintVerboseRule.code, ReplaceWithPrintV.new); + } +} diff --git a/cw_custom_lints/lib/print_verbose/print_verbose_fix.dart b/cw_custom_lints/lib/print_verbose/print_verbose_fix.dart new file mode 100644 index 0000000000..b7a2daf52c --- /dev/null +++ b/cw_custom_lints/lib/print_verbose/print_verbose_fix.dart @@ -0,0 +1,50 @@ +import "package:analysis_server_plugin/edit/dart/correction_producer.dart"; +import "package:analysis_server_plugin/edit/dart/dart_fix_kind_priority.dart"; +import "package:analyzer/dart/ast/ast.dart"; +import "package:analyzer_plugin/utilities/change_builder/change_builder_core.dart"; +import "package:analyzer_plugin/utilities/fixes/fixes.dart"; +import "package:analyzer_plugin/utilities/range_factory.dart"; + +class ReplaceWithPrintV extends ResolvedCorrectionProducer { + ReplaceWithPrintV({required super.context}); + + static const _importString = "package:cw_core/utils/print_verbose.dart"; + + static const _fixKind = FixKind( + "dart.fix.replaceWithPrintV", + DartFixKindPriority.standard, + "Replace with printV()", + ); + + @override + CorrectionApplicability get applicability => + CorrectionApplicability.singleLocation; + + @override + FixKind get fixKind => _fixKind; + + @override + Future compute(ChangeBuilder builder) async { + final invocation = node.thisOrAncestorOfType(); + if (invocation == null) { + return; + } + + final root = invocation.root; + final hasImport = root is CompilationUnit && + root.directives.whereType().any( + (directive) => directive.uri.stringValue == _importString, + ); + + await builder.addDartFileEdit(file, (builder) { + builder.addSimpleReplacement( + range.node(invocation.methodName), + "printV", + ); + + if (!hasImport) { + builder.importLibrary(Uri.parse(_importString)); + } + }); + } +} diff --git a/cw_custom_lints/lib/print_verbose/print_verbose_rule.dart b/cw_custom_lints/lib/print_verbose/print_verbose_rule.dart new file mode 100644 index 0000000000..0fee39bc62 --- /dev/null +++ b/cw_custom_lints/lib/print_verbose/print_verbose_rule.dart @@ -0,0 +1,52 @@ +import "package:analyzer/analysis_rule/analysis_rule.dart"; +import "package:analyzer/analysis_rule/rule_context.dart"; +import "package:analyzer/analysis_rule/rule_visitor_registry.dart"; +import "package:analyzer/dart/ast/ast.dart"; +import "package:analyzer/dart/ast/visitor.dart"; +import "package:analyzer/error/error.dart"; + +class PrintVerboseRule extends AnalysisRule { + PrintVerboseRule() + : super( + name: "use_print_v", + description: "Use printV() from cw_core instead of print().", + ); + + static const LintCode code = LintCode( + "use_print_v", + "Use printV() from cw_core instead", + correctionMessage: "Replace print with printV", + severity: DiagnosticSeverity.WARNING, + ); + + @override + LintCode get diagnosticCode => code; + + @override + void registerNodeProcessors( + RuleVisitorRegistry registry, + RuleContext context, + ) { + final filePath = context.definingUnit.file.path; + + if (filePath.contains("/tool/") || filePath.contains("print_verbose.dart")) { + // tool/ is allowed to use print as it never makes its way into the app + return; + } + + registry.addMethodInvocation(this, _Visitor(this)); + } +} + +class _Visitor extends SimpleAstVisitor { + _Visitor(this.rule); + + final AnalysisRule rule; + + @override + void visitMethodInvocation(MethodInvocation node) { + if (node.methodName.name == "print" && node.target == null) { + rule.reportAtNode(node); + } + } +} diff --git a/cw_custom_lints/lib/restricted_imports/restricted_imports_rule.dart b/cw_custom_lints/lib/restricted_imports/restricted_imports_rule.dart new file mode 100644 index 0000000000..80680af87f --- /dev/null +++ b/cw_custom_lints/lib/restricted_imports/restricted_imports_rule.dart @@ -0,0 +1,79 @@ +import "package:analyzer/analysis_rule/analysis_rule.dart"; +import "package:analyzer/analysis_rule/rule_context.dart"; +import "package:analyzer/analysis_rule/rule_visitor_registry.dart"; +import "package:analyzer/dart/ast/ast.dart"; +import "package:analyzer/dart/ast/visitor.dart"; +import "package:analyzer/error/error.dart"; + +class RestrictedImportsRule extends AnalysisRule { + RestrictedImportsRule() + : super( + name: "no_restricted_imports_in_lib", + description: + "Do not import from coin-specific packages. If necessary, wire your logic through configure.dart", + ); + + static const LintCode code = LintCode( + "no_restricted_imports_in_lib", + "Do not import from coin-specific packages. If necessary, wire your logic through configure.dart", + severity: DiagnosticSeverity.ERROR, + ); + + static const _restrictedPackages = { + "cw_bitcoin", + "cw_bitcoin_cash", + "cw_electrum", + "cw_litecoin", + "cw_evm", + "cw_mweb", + "cw_nano", + "cw_solana", + "cw_tron", + "cw_zano", + }; + + @override + LintCode get diagnosticCode => code; + + @override + void registerNodeProcessors( + RuleVisitorRegistry registry, + RuleContext context, + ) { + final filePath = context.definingUnit.file.path; + final fileName = filePath.split("/").last; + + // ignore cw_something folders + if (filePath.contains("cw_")) { + return; + } + + // allow ex. cw_bitcoin in bitcoin.dart + if (_restrictedPackages.contains("cw_" + fileName.replaceAll(".dart", ""))) { + return; + } + + registry.addImportDirective(this, _Visitor(this)); + } +} + +class _Visitor extends SimpleAstVisitor { + _Visitor(this.rule); + + final AnalysisRule rule; + + @override + void visitImportDirective(ImportDirective node) { + final uriString = node.uri.stringValue; + if (uriString == null || !uriString.startsWith("package:")) { + return; + } + + final pathWithoutScheme = uriString.replaceFirst("package:", ""); + final packageName = pathWithoutScheme.split("/").first; + + if (RestrictedImportsRule._restrictedPackages.contains(packageName)) { + rule.reportAtNode(node); + } + } +} diff --git a/cw_custom_lints/pubspec.yaml b/cw_custom_lints/pubspec.yaml new file mode 100644 index 0000000000..399fa59c67 --- /dev/null +++ b/cw_custom_lints/pubspec.yaml @@ -0,0 +1,15 @@ +name: cw_custom_lints +version: 0.0.1 +environment: + sdk: '>=3.9.0 <4.0.0' + + +dependencies: + analysis_server_plugin: 0.3.15 + analyzer: 13.0.0 + analyzer_plugin: 0.14.9 + +dev_dependencies: + analyzer_testing: 0.2.6 + test_reflective_loader: 0.4.0 + test: 1.31.2 diff --git a/cw_decred/analysis_options.yaml b/cw_decred/analysis_options.yaml deleted file mode 100644 index a5744c1cfb..0000000000 --- a/cw_decred/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/cw_dogecoin/analysis_options.yaml b/cw_dogecoin/analysis_options.yaml deleted file mode 100644 index a5744c1cfb..0000000000 --- a/cw_dogecoin/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/cw_evm/analysis_options.yaml b/cw_evm/analysis_options.yaml deleted file mode 100644 index a5744c1cfb..0000000000 --- a/cw_evm/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/cw_monero/analysis_options.yaml b/cw_monero/analysis_options.yaml deleted file mode 100644 index a5744c1cfb..0000000000 --- a/cw_monero/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/cw_mweb/analysis_options.yaml b/cw_mweb/analysis_options.yaml deleted file mode 100644 index a5744c1cfb..0000000000 --- a/cw_mweb/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/cw_nano/pubspec.yaml b/cw_nano/pubspec.yaml index a3ef2ea67e..449b1b8f54 100644 --- a/cw_nano/pubspec.yaml +++ b/cw_nano/pubspec.yaml @@ -6,7 +6,7 @@ author: Cake Wallet homepage: https://cakewallet.com environment: - sdk: '>=2.18.2 <3.0.0' + sdk: '>=3.0.0 <4.0.0' flutter: ">=1.17.0" dependencies: diff --git a/cw_solana/analysis_options.yaml b/cw_solana/analysis_options.yaml deleted file mode 100644 index a5744c1cfb..0000000000 --- a/cw_solana/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/cw_tron/analysis_options.yaml b/cw_tron/analysis_options.yaml deleted file mode 100644 index a5744c1cfb..0000000000 --- a/cw_tron/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/cw_wownero/analysis_options.yaml b/cw_wownero/analysis_options.yaml deleted file mode 100644 index a5744c1cfb..0000000000 --- a/cw_wownero/analysis_options.yaml +++ /dev/null @@ -1,4 +0,0 @@ -include: package:flutter_lints/flutter.yaml - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/cw_zano/pubspec.yaml b/cw_zano/pubspec.yaml index 7aa32af60b..7e8ede8dd0 100644 --- a/cw_zano/pubspec.yaml +++ b/cw_zano/pubspec.yaml @@ -5,7 +5,7 @@ publish_to: none homepage: https://cakewallet.com environment: - sdk: ">=2.19.0 <3.0.0" + sdk: ">=3.0.0 <4.0.0" flutter: ">=1.20.0" dependencies: diff --git a/cw_zcash/analysis_options.yaml b/cw_zcash/analysis_options.yaml deleted file mode 100644 index f43bd483da..0000000000 --- a/cw_zcash/analysis_options.yaml +++ /dev/null @@ -1,31 +0,0 @@ -analyzer: - errors: - implementation_imports: ignore - must_be_immutable: ignore - overridden_fields: ignore - exclude: - - "**.g.dart" - - "external/**" - -linter: - rules: - unawaited_futures: true - avoid_print: false - parameter_assignments: true - prefer_const_declarations: true - prefer_final_fields: true - prefer_final_in_for_each: true - prefer_final_locals: true - prefer_final_parameters: true - avoid_void_async: true - require_trailing_commas: true - sort_child_properties_last: true - sort_constructors_first: true - sort_unnamed_constructors_first: true - unnecessary_async: true - unnecessary_await_in_return: true - unnecessary_string_interpolations: true - always_declare_return_types: true - -formatter: - page_width: 100 diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index a5d1183226..724c9197f2 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -177,7 +177,7 @@ class CWBitcoin extends Bitcoin { List getSubAddresses(Object wallet) { final electrumWallet = wallet as ElectrumWallet; return electrumWallet.walletAddresses.addressesByReceiveType - .map((BaseBitcoinAddressRecord addr) => ElectrumSubAddress( + .map((addr) => ElectrumSubAddress( id: addr.index, name: addr.name, address: addr.address, diff --git a/scripts/lint.sh b/scripts/lint.sh index c4b1b4e4f6..cfffdb8010 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -1,17 +1,29 @@ #!/bin/bash -set -x -e -cd $(dirname $0) +set -e +cd "$(dirname "$0")" cd .. -for i in cw_zcash; +for i in cw_bitcoin cw_bitcoin_cash cw_core cw_decred cw_dogecoin cw_evm cw_monero cw_mweb cw_nano cw_solana cw_tron cw_zano cw_zcash lib; do - dart fix --apply $i/ - dart format --line-length=100 $i/ - if [[ ! "x$ABORT_ON_CHANGE" == "x" ]]; - then - if [[ ! -z "$(git status --porcelain -- $(find $i))" ]]; - then - echo "Please run scripts/lint.sh ($i has changes)" + { + # files modified after the lints got enabled and committed + git log --since="2026-07-14 23:59:59" --name-only --diff-filter=d --format="" -- "$i" + # uncommitted files (assumed to have been modified after the 29th) + git diff --name-only --diff-filter=d HEAD -- "$i" + } | grep '\.dart$' | sort -u | while IFS= read -r file; do + echo $file + if [[ -f "$file" ]]; then + dart fix --apply "$file" && dart format --line-length=100 "$file" + fi + done + + if [[ -n "$ABORT_ON_CHANGE" ]]; then + DART_CHANGES=$(git status --porcelain -- "$i" | grep '\.dart$' || true) + + if [[ -n "$DART_CHANGES" ]]; then + echo "Please run scripts/lint.sh" + echo "changes in $i:" + echo "$DART_CHANGES" exit 1 fi fi diff --git a/tool/append_translation.dart b/tool/append_translation.dart index 7232f308b9..1cc6336b85 100644 --- a/tool/append_translation.dart +++ b/tool/append_translation.dart @@ -15,7 +15,7 @@ void main(List args) async { final text = args[1]; final force = args.last == "--force"; - printV('Appending "$name": "$text"'); + print('Appending "$name": "$text"'); // add translation to all languages: for (var lang in langs) { @@ -25,12 +25,12 @@ void main(List args) async { appendStringToArbFile(fileName, name, translation, force: force); } - printV('Alphabetizing all files...'); + print('Alphabetizing all files...'); for (var lang in langs) { final fileName = getArbFileName(lang); alphabetizeArbFile(fileName); } - printV('Done!'); + print('Done!'); } \ No newline at end of file diff --git a/tool/download_moneroc_prebuilds.dart b/tool/download_moneroc_prebuilds.dart index a12777ad56..dee0461240 100644 --- a/tool/download_moneroc_prebuilds.dart +++ b/tool/download_moneroc_prebuilds.dart @@ -31,7 +31,7 @@ Future main() async { final resp = await _dio.get("https://api.github.com/repos/mrcyjanek/monero_c/releases"); final data = resp.data[0]; final tagName = data['tag_name']; - printV("Downloading artifacts for: ${tagName}"); + print("Downloading artifacts for: ${tagName}"); final assets = data['assets'] as List; for (var i = 0; i < assets.length; i++) { for (var triplet in triplets) { @@ -42,10 +42,10 @@ Future main() async { String localFilename = filename.replaceAll("${coin}_${triplet}_", ""); localFilename = "scripts/monero_c/release/${coin}/${triplet}_${localFilename}"; final url = asset["browser_download_url"] as String; - printV("- downloading $localFilename"); + print("- downloading $localFilename"); await _dio.download(url, localFilename); if (localFilename.endsWith(".xz")) { - printV(" extracting $localFilename"); + print(" extracting $localFilename"); final inputStream = InputFileStream(localFilename); final archive = XZDecoder().decodeBytes(inputStream.toUint8List()); final outputStream = OutputFileStream(localFilename.replaceAll(".xz", "")); @@ -54,11 +54,11 @@ Future main() async { } } if (Platform.isMacOS) { - printV("Generating ios framework"); + print("Generating ios framework"); final result = Process.runSync("bash", [ "-c", "cd scripts/ios && ./gen_framework.sh && cd ../.." ]); - printV((result.stdout+result.stderr).toString().trim()); + print((result.stdout+result.stderr).toString().trim()); } } \ No newline at end of file diff --git a/tool/fiat_api_check.dart b/tool/fiat_api_check.dart index 5f3fe49078..c70fa33728 100755 --- a/tool/fiat_api_check.dart +++ b/tool/fiat_api_check.dart @@ -76,7 +76,7 @@ void main() { // --- B. Run App in a Zone to Capture Prints --- runZoned( () async { - printV('--- Starting Verified Price Check ---'); + print('--- Starting Verified Price Check ---'); final Map> workingPairs = {}; final Map> failedPairs = {}; @@ -130,7 +130,7 @@ void main() { } // Print immediate status (Captured by Zone) - printV('$logPrefix $logMessage'); + print('$logPrefix $logMessage'); // Aggregate if (isSuccess) { @@ -148,25 +148,25 @@ void main() { } // --- FINAL SUMMARY --- - printV('\n\n=== SUMMARY ===\n'); + print('\n\n=== SUMMARY ===\n'); // Print Successful workingPairs.forEach((crypto, fiats) { if (fiats.isNotEmpty) { - printV('✅ ${crypto.toUpperCase()}: ${fiats.join(", ")}'); + print('✅ ${crypto.toUpperCase()}: ${fiats.join(", ")}'); } }); - printV('\n--------------------------------------------------\n'); + print('\n--------------------------------------------------\n'); // Print Failed failedPairs.forEach((crypto, fiats) { if (fiats.isNotEmpty) { - printV('❌ ${crypto.toUpperCase()}: ${fiats.join(", ")}'); + print('❌ ${crypto.toUpperCase()}: ${fiats.join(", ")}'); } }); - printV('\n=== DONE ==='); + print('\n=== DONE ==='); }, // --- C. The Interceptor --- diff --git a/tool/generate_localization.dart b/tool/generate_localization.dart index 419539566f..4b0927a811 100644 --- a/tool/generate_localization.dart +++ b/tool/generate_localization.dart @@ -37,7 +37,7 @@ Future main(List args) async { extraInfo.forEach((key, dynamic value) async { if (key != srcDir) { - printV('Wrong key: $key'); + print('Wrong key: $key'); return; } @@ -45,7 +45,7 @@ Future main(List args) async { final dir = Directory(dirPath); if (!await dir.exists()) { - printV('Wrong directory path: $dirPath'); + print('Wrong directory path: $dirPath'); return; } @@ -55,7 +55,7 @@ Future main(List args) async { // normalizing the case so keys match LanguageService.supportedLocales. final fileName = element.uri.pathSegments.last; if (!fileName.startsWith('strings_') || !fileName.endsWith('.arb')) { - printV('Wrong file: ${element.path}'); + print('Wrong file: ${element.path}'); return; } final parts = @@ -67,7 +67,7 @@ Future main(List args) async { }); if (!localePath.keys.contains(defaultLocale)) { - printV("Locale list doesn't contain $defaultLocale"); + print("Locale list doesn't contain $defaultLocale"); return; } @@ -124,7 +124,7 @@ Future main(List args) async { await File(outputPath + localeListFileName).writeAsString(locales); } catch (e) { - printV(e.toString()); + print(e.toString()); } }); } diff --git a/tool/print_verbose_dummy.dart b/tool/print_verbose_dummy.dart index f7286e1f9f..e69de29bb2 100644 --- a/tool/print_verbose_dummy.dart +++ b/tool/print_verbose_dummy.dart @@ -1 +0,0 @@ -void printV(dynamic content) => print(content); \ No newline at end of file diff --git a/tool/translate_changelog.dart b/tool/translate_changelog.dart index e044cc2088..609c969a60 100644 --- a/tool/translate_changelog.dart +++ b/tool/translate_changelog.dart @@ -46,7 +46,7 @@ void main() async { }); currentItem++; - printV( + print( "\r$currentLang/${langs.length} langs done, $currentItem/${defaultLangChangelog.length} items done"); } currentLang++; diff --git a/tool/translate_using_llm.dart b/tool/translate_using_llm.dart index 3ca92d784d..e5c22efe1a 100644 --- a/tool/translate_using_llm.dart +++ b/tool/translate_using_llm.dart @@ -25,18 +25,18 @@ class TranslationChecker { final sourceMap = json.decode(sourceContent) as Map; final destinationMap = json.decode(destinationContent) as Map; - printV('Found ${sourceMap.length} keys in source, ${destinationMap.length} in destination'); + print('Found ${sourceMap.length} keys in source, ${destinationMap.length} in destination'); final keysToProcess = specificKey != null ? (sourceMap.containsKey(specificKey) ? [specificKey] : []) : sourceMap.keys.toList(); if (specificKey != null && keysToProcess.isEmpty) { - printV('Error: Key "$specificKey" not found in source file'); + print('Error: Key "$specificKey" not found in source file'); return; } - printV('Processing ${keysToProcess.length} translations...'); + print('Processing ${keysToProcess.length} translations...'); int processed = 0; int corrected = 0; @@ -57,16 +57,16 @@ class TranslationChecker { if (correctedTranslation != destinationValue) { destinationMap[key] = correctedTranslation; corrected++; - printV('Processed: "$key" -> CORRECTED'); - printV(' - eng : "$sourceValue"'); - printV(' - dst orig: "$destinationValue"'); - printV(' - dst new : "$correctedTranslation"'); + print('Processed: "$key" -> CORRECTED'); + print(' - eng : "$sourceValue"'); + print(' - dst orig: "$destinationValue"'); + print(' - dst new : "$correctedTranslation"'); await _writeArbFile(destinationArbPath, destinationMap); } else { - printV('Processed: "$key" -> VERIFIED'); + print('Processed: "$key" -> VERIFIED'); } } else { - printV('Processed: "$key" -> SKIPPED (non-string)'); + print('Processed: "$key" -> SKIPPED (non-string)'); } processed++; @@ -74,11 +74,11 @@ class TranslationChecker { await _writeArbFile(destinationArbPath, destinationMap); - printV(''); - printV('Summary:'); - printV('Processed: $processed keys'); - printV('Corrected: $corrected keys'); - printV('Updated: $destinationArbPath'); + print(''); + print('Summary:'); + print('Processed: $processed keys'); + print('Corrected: $corrected keys'); + print('Updated: $destinationArbPath'); } Future _checkSingleTranslation({ @@ -108,7 +108,7 @@ class TranslationChecker { return currentTranslation; } catch (e) { - printV('Error checking translation for key "$key": $e'); + print('Error checking translation for key "$key": $e'); return currentTranslation; } } @@ -186,7 +186,7 @@ IMPORTANT: Respond with a JSON object in the exact format, you must think about final jsonEnd = response.lastIndexOf('}'); if (jsonStart == -1 || jsonEnd == -1 || jsonStart >= jsonEnd) { - printV('No valid JSON found in LLM response'); + print('No valid JSON found in LLM response'); return null; } @@ -195,7 +195,7 @@ IMPORTANT: Respond with a JSON object in the exact format, you must think about return parsed['corrected_translation'] as String?; } catch (e) { - printV('Error extracting JSON from LLM response: $e'); + print('Error extracting JSON from LLM response: $e'); return null; } } @@ -228,7 +228,7 @@ void main(List args) async { final results = parser.parse(args); if (results['help'] as bool) { - printV(parser.usage); + print(parser.usage); return; } @@ -239,24 +239,24 @@ void main(List args) async { final model = results['model'] as String; if (!File(sourcePath).existsSync()) { - printV('Error: Source ARB file not found: $sourcePath'); + print('Error: Source ARB file not found: $sourcePath'); exit(1); } if (!File(destinationPath).existsSync()) { - printV('Error: Destination ARB file not found: $destinationPath'); + print('Error: Destination ARB file not found: $destinationPath'); exit(1); } - printV('Translation Checker'); - printV('Source: $sourcePath'); - printV('Destination: $destinationPath'); - printV('Ollama URL: $ollamaUrl'); - printV('Model: $model'); + print('Translation Checker'); + print('Source: $sourcePath'); + print('Destination: $destinationPath'); + print('Ollama URL: $ollamaUrl'); + print('Model: $model'); if (specificKey != null) { - printV('Key: $specificKey'); + print('Key: $specificKey'); } - printV(''); + print(''); final checker = TranslationChecker( ollamaBaseUrl: ollamaUrl, @@ -270,8 +270,8 @@ void main(List args) async { ); } catch (e) { - printV('Error: $e'); - printV('Run with --help for more information.'); + print('Error: $e'); + print('Run with --help for more information.'); exit(1); } } diff --git a/tool/translation_add_lang.dart b/tool/translation_add_lang.dart index e3c372a8c7..3f509090e5 100644 --- a/tool/translation_add_lang.dart +++ b/tool/translation_add_lang.dart @@ -34,5 +34,5 @@ void main(List args) async { } appendStringsToArbFile(targetFileName, translations); - printV("Success! Please add your Language Code to lib/entities/language_service.dart"); + print("Success! Please add your Language Code to lib/entities/language_service.dart"); } diff --git a/tool/translation_consistence.dart b/tool/translation_consistence.dart index 2cdd358c8b..f3d3120b02 100644 --- a/tool/translation_consistence.dart +++ b/tool/translation_consistence.dart @@ -7,14 +7,14 @@ import 'utils/translation/translation_constants.dart'; import 'utils/translation/translation_utils.dart'; void main(List args) async { - printV('Checking Consistency of all arb-files. Default: $defaultLang'); + print('Checking Consistency of all arb-files. Default: $defaultLang'); final doFix = args.contains("--fix"); if (doFix) - printV('Auto fixing enabled!\n'); + print('Auto fixing enabled!\n'); else - printV('Auto fixing disabled!\nRun with arg "--fix" to enable autofix\n'); + print('Auto fixing disabled!\nRun with arg "--fix" to enable autofix\n'); final fileName = getArbFileName(defaultLang); final file = File(fileName); @@ -27,7 +27,7 @@ void main(List args) async { final missingDefaults = {}; missingKeys.forEach((key) { - printV('Missing in "$lang": "$key"'); + print('Missing in "$lang": "$key"'); if (doFix) missingDefaults[key] = arbObj[key] as String; }); diff --git a/tool/utils/translation/arb_file_utils.dart b/tool/utils/translation/arb_file_utils.dart index 92a8f6e2e8..c8633c09cf 100644 --- a/tool/utils/translation/arb_file_utils.dart +++ b/tool/utils/translation/arb_file_utils.dart @@ -8,7 +8,7 @@ void appendStringToArbFile(String fileName, String name, String text, {bool forc final arbObj = readArbFile(file); if (arbObj.containsKey(name) && !force) { - printV("String $name already exists in $fileName!"); + print("String $name already exists in $fileName!"); return; } diff --git a/tool/utils/translation/translation_utils.dart b/tool/utils/translation/translation_utils.dart index f53a8d13ab..4498e279a6 100644 --- a/tool/utils/translation/translation_utils.dart +++ b/tool/utils/translation/translation_utils.dart @@ -38,7 +38,7 @@ Future getTranslation(String text, String lang) async { try { translation = (await translator.translate(text, from: defaultLang, to: lang)).text; } catch (e) { - printV("$lang, $text, $e"); + print("$lang, $text, $e"); } placeholder.forEach((index, value) { final translatedPlaceholder = regExp.allMatches(translation).toList()[index];