Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
14be833
Add cyclomatic complexity linter configuration for camera_android_cam…
reidbaker Jun 25, 2026
f206376
Configure CI target and script to run cyclomatic complexity linter
reidbaker Jun 25, 2026
0299ea1
Integrate cyclomatic complexity check directly into flutter_plugin_to…
reidbaker Jun 25, 2026
30757fa
Remove commented-out plugin lines from analysis_options.yaml
reidbaker Jun 25, 2026
fc676e2
Refactor analyze_command linter check to a separate helper method and…
reidbaker Jun 25, 2026
e82d111
Refactor custom linter checks into a pipeline runner list for easier …
reidbaker Jun 25, 2026
5e798fe
Clean up custom linter API boundaries by extracting dependency checki…
reidbaker Jun 25, 2026
8b336a2
Clean up static analysis warnings in analyze_command and analyze_comm…
reidbaker Jun 25, 2026
2f96c6b
Restrict custom linter checks to dev_dependencies only
reidbaker Jun 25, 2026
84abbf0
Use expectedInitialArgs to explicitly document mocks in linter tests
reidbaker Jun 25, 2026
eb76af7
Improve tests by explicitly mocking process queues and verifying cons…
reidbaker Jun 25, 2026
d93d538
De-duplicate pubspec writing and mock process logic in linter tests u…
reidbaker Jun 25, 2026
dd58a0d
Ensure linter skip tests are robust against print string changes by a…
reidbaker Jun 25, 2026
214f051
Add descriptive doc comments and dynamic Dart/Flutter SDK resolution …
reidbaker Jun 25, 2026
71559a9
Update custom_analysis.yaml comment to say cyclomatic complexity
reidbaker Jun 25, 2026
24d9e8a
Simplify custom linter checks to always execute via dart run
reidbaker Jun 25, 2026
ea9770a
Add defensive check and tests to ensure linter does not run on packag…
reidbaker Jun 25, 2026
e9bdaeb
Address review feedback: rename mock helper and report cyclomatic com…
reidbaker Jun 30, 2026
342eab8
Fix formatting and linter issues in analyze command changes
reidbaker Jun 30, 2026
64a8a90
Refactor custom linter checking to aggregate errors instead of failin…
reidbaker Jul 1, 2026
db7e807
Merge branch 'main' into add-cyclomatic-complexity-linter
reidbaker Jul 1, 2026
f745d82
Fix early return on standard analysis failure and add combined aggreg…
reidbaker Jul 1, 2026
d14f6e1
Rename combined failures integration test for clarity
reidbaker Jul 1, 2026
002182d
Fix dart_code_linter compile error during pub downgrade by pinning fi…
reidbaker Jul 1, 2026
c1247d4
Revert "Fix dart_code_linter compile error during pub downgrade by pi…
reidbaker Jul 1, 2026
b934bf8
Skip custom linter checks during downgraded analyze runs
reidbaker Jul 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .repo_tool_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ allowed_dependencies:
# A pin can be either an exact version, or a range with an explicit, inclusive
# max version, which must a version that already exists, not a future version.
pinned:
# Cyclomatic complexity linter for camera_android_camerax
- dart_code_linter

# Test-only dependency, so does not impact package clients, and
# has limited impact so could be easily removed if there are
# ever maintenance issues in the future.
Expand Down
16 changes: 16 additions & 0 deletions packages/camera/camera_android_camerax/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
include: ../../../analysis_options.yaml

analyzer:
exclude:
- build/**
- android/**
- ios/**
- web/**
- windows/**
- macos/**
- linux/**

dart_code_linter:
metrics:
cyclomatic-complexity: 15

1 change: 1 addition & 0 deletions packages/camera/camera_android_camerax/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ dependencies:

dev_dependencies:
build_runner: ^2.2.0
dart_code_linter: 4.1.5

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've audited this as safe to run in our CI and locally?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SHA1 (dart_code_linter-4.1.5.tar.gz) = 86fa1bd240a88c98a4c45f0beb43b4e7adb32735
For auditability this this is the artifact I inspected.

Looking at the code I didn't see anything suspicious.

Some things I found but I do not think are blocking:
It uses https://pub.dev/packages/pub_updater to see if there is a newer version. It works without internet access.
There is a python script to make cursor skills. It is for contributors but I found that interesting.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As concrete examples of suspicious things I looked for. Code that tried to spawn other process, code that tried to install other things, network calls, code that read env variables that looked like secrets.

dart_skills_lint:
git:
url: https://github.com/flutter/skills.git
Expand Down
3 changes: 3 additions & 0 deletions script/configs/custom_analysis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@
- rfw/example
# Disables docs requirements, as it is test code.
- web_benchmarks/testing/test_app
# Uses custom analysis to enable the cyclomatic complexity linter plugin.
- camera/camera_android_camerax

86 changes: 83 additions & 3 deletions script/tool/lib/src/analyze_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'dart:io' as io;

import 'package:file/file.dart';
import 'package:yaml/yaml.dart';

import 'common/core.dart';
import 'common/file_filters.dart';
Expand Down Expand Up @@ -330,10 +331,75 @@ class AnalyzeCommand extends PackageLoopingCommand {
...skillsErrors,
];

if (errors.isNotEmpty) {
return PackageResult.fail(errors);
final customCheckRunners = <_CustomLinter>[

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we need this since we're only adding one linter at the moment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am expecting we will expand this list. I would like to keep the functionality for now.

_CustomLinter(dependencyName: 'dart_code_linter', run: _runDartCodeLinterForPackage),
];

// Skip custom linters during downgrade as metrics are redundant and vulnerable to dependency issues.
if (!getBoolArg(_downgradeFlag)) {
final Pubspec pubspec = package.parsePubspec();
for (final runner in customCheckRunners) {
final bool hasDependency = pubspec.devDependencies.containsKey(runner.dependencyName);
if (hasDependency) {
errors.addAll(await runner.run(package));
}
}
}
return PackageResult.success();

return errors.isEmpty ? PackageResult.success() : PackageResult.fail(errors);
}

/// Retrieves the configured cyclomatic complexity threshold from the local
/// `analysis_options.yaml` if it exists and is configured.
int? _getLinterThreshold(RepositoryPackage package) {
final File optionsFile = package.directory.childFile('analysis_options.yaml');
if (!optionsFile.existsSync()) {
return null;
}
try {
final Object? yaml = loadYaml(optionsFile.readAsStringSync());
if (yaml is YamlMap) {
final Object? linter = yaml['dart_code_linter'];
if (linter is YamlMap) {
final Object? metrics = linter['metrics'];
if (metrics is YamlMap) {
final Object? complexity = metrics['cyclomatic-complexity'];
if (complexity is int) {
return complexity;
}
}
}
}
} catch (_) {
// Ignore errors parsing invalid/incomplete files.
}
return null;
}

/// Runs the `dart_code_linter` metrics analyzer on the package.
///
/// Assumes `dart_code_linter` is present in `dev_dependencies`.
Future<List<String>> _runDartCodeLinterForPackage(RepositoryPackage package) async {
if (!package.libDirectory.existsSync()) {
return <String>[];
}
print('Running dart_code_linter:metrics analysis...');
final int linterExitCode = await processRunner.runAndStream(_dartBinaryPath, <String>[
'run',
'dart_code_linter:metrics',
'analyze',
'lib',
'--set-exit-on-violation-level=warning',
], workingDir: package.directory);
if (linterExitCode != 0) {
final int? threshold = _getLinterThreshold(package);
final thresholdMessage = threshold != null ? ' (configured threshold: $threshold)' : '';
return <String>[
'Metrics violations found$thresholdMessage. See the package\'s local "analysis_options.yaml" for configured thresholds.',
];
}

return <String>[];
}

/// Validates that `.agents/skills` contains Dart code if configured for skills analysis.
Expand Down Expand Up @@ -485,3 +551,17 @@ class AnalyzeCommand extends PackageLoopingCommand {
return errors.isEmpty ? PackageResult.success() : PackageResult.fail(errors);
}
}

/// Represents a custom linter check that is executed during package analysis.
class _CustomLinter {
const _CustomLinter({required this.dependencyName, required this.run});

/// The name of the package dependency that triggers this custom check.
///
/// The check is only executed if this dependency is listed in the package's
/// `dev_dependencies`.
final String dependencyName;

/// The runner function that executes the custom check.
final Future<List<String>> Function(RepositoryPackage) run;
}
Loading
Loading