add-lints (#3356)

* test add lint as separate action * nicer logging * only detect changes in dart files * fix printV warnings being displayed when editing printV --------- Co-authored-by: Czarek Nakamoto <cyjan@mrcyjanek.net>

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