Skip to content

Add pre-commit hook to verify static analysis passes & format is correct#11941

Open
camsim99 wants to merge 23 commits into
flutter:mainfrom
camsim99:cos_precommit
Open

Add pre-commit hook to verify static analysis passes & format is correct#11941
camsim99 wants to merge 23 commits into
flutter:mainfrom
camsim99:cos_precommit

Conversation

@camsim99

@camsim99 camsim99 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Adds a pre-commit hook that you can install to verify that dart analyze --fatal-infos and the package format are correct when you make a call to git commit. Intended to ensure agents commit code that meets are quality standards.

Miscellaneous notes:

  • script/githooks/bin/install_hooks.dart is the script to install the hook that devs would need to run to use it.
  • I modeled the new githooks directory like https://github.com/flutter/flutter/tree/master/engine/src/flutter/tools/githooks to make it easier for folks to add new hooks
  • The hook separates formatting of Dart and native files as an optimization to avoid all of the configuration that the plugin tool uses to format native files that are not required if no native files have been changed

Pre-Review Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

Footnotes

  1. Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. 2

@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Jun 18, 2026
@github-actions github-actions Bot removed the CICD Run CI/CD label Jun 22, 2026
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Jun 22, 2026
@camsim99 camsim99 added CICD Run CI/CD and removed CICD Run CI/CD labels Jun 22, 2026
@camsim99 camsim99 marked this pull request as ready for review June 22, 2026 17:58

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request introduces a new githooks Dart package to automate pre-commit checks, including formatting and static analysis, for staged files. The package includes an installation script, a pre-commit command runner, and associated unit tests. Review feedback highlights a potential infinite loop on Windows when locating the .git directory, raw ANSI escape sequences being printed without verifying terminal support, the use of loose any version constraints in pubspec.yaml, and redundant dead code in the test mocks.

Comment thread script/githooks/bin/install_hooks.dart
Comment thread script/githooks/lib/src/pre_commit_command.dart
Comment thread script/githooks/lib/src/pre_commit_command.dart
Comment thread script/githooks/lib/src/pre_commit_command.dart
Comment thread script/githooks/pubspec.yaml
Comment thread script/githooks/test/pre_commit_test.dart
Comment thread script/githooks/test/pre_commit_test.dart
Comment thread script/githooks/test/pre_commit_test.dart
Comment thread script/githooks/test/pre_commit_test.dart
@camsim99 camsim99 removed the CICD Run CI/CD label Jun 22, 2026
@camsim99 camsim99 added the CICD Run CI/CD label Jun 22, 2026
@camsim99

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request introduces a Dart-based pre-commit git hook framework to automate formatting and static analysis checks on staged files. The feedback suggests enhancing compatibility with Git submodules and worktrees by checking for .git as a file, adding support for .hpp extensions in Clang formatting, conditionally writing ANSI escape sequences based on terminal support, and adding test coverage for the native formatting execution path.

Comment thread script/githooks/bin/install_hooks.dart
Comment thread script/githooks/lib/src/pre_commit_command.dart
Comment thread script/githooks/lib/src/pre_commit_command.dart
Comment thread script/githooks/lib/src/pre_commit_command.dart
Comment thread script/githooks/lib/src/pre_commit_command.dart
Comment thread script/githooks/lib/src/pre_commit_command.dart
Comment thread script/githooks/test/pre_commit_test.dart
@github-actions github-actions Bot removed the CICD Run CI/CD label Jun 22, 2026
@camsim99 camsim99 added the CICD Run CI/CD label Jun 22, 2026
@camsim99 camsim99 requested a review from reidbaker June 22, 2026 22:45

@reidbaker reidbaker left a comment

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.

I left a bunch of comments on the code but before addressing any of those 2 high level questions.

Why did you rewrite analyze and format logic instead of using the logic that is already built into the flutter packages tooling?
If we were not going to use the existing flutter packages tooling for some good technical reason why not use something like https://github.com/pre-commit/pre-commit

'--diff-filter=ACM',
], workingDirectory: repoRoot.path);

if (diffResult.exitCode != 0) {

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.

With your ACM filter there could be a commit that was just a deletion but I dont think it would give any files. What error code is returned in that situation or is that a success? Looks like empty sets are handled below.

], workingDirectory: repoRoot.path);

if (diffResult.exitCode != 0) {
print('❌ Failed to get staged files');

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.

Do you think we should include the command that failed and the error code?

}

@override
Future<bool> run() async {

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.

Add dart doc

}

if (targetPackageDirs.isEmpty) {
// None of the changed files are part of a package we care about.

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.

I am a bit confused as to what this does. It looks like it is building a set of packages from staged files. But if that is the case then where does the filtering happen.

final packageArgs = '--packages=${targetPackages.join(',')}';

// Determine which toolchains are needed based on file extensions to avoid uneccessary slowdown.
final bool hasClang = stagedFiles.any(

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.

What code determines if this list needs to change?

f.endsWith('.m') ||
f.endsWith('.mm'),
);
final bool hasJava = stagedFiles.any((f) => f.endsWith('.java'));

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.

Recommend structuring this a bit more formally. What information is needed per language? How do you execute per language. Lets make the blast radius clear and the file manipulations happen outside of any language logic.

// Format staged native files.
final bool needsNativeFormat = hasClang || hasJava || hasKotlin || hasSwift;
if (needsNativeFormat) {
final nativeFormatFlags = [

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.

Lets use the args package. https://pub.dev/packages/args

print(nativeFormatResult.stderr);
}
print(
'❌ Formatting issues found in native files. Please run "dart run script/tool/bin/flutter_plugin_tools.dart format $packageArgs" to fix them.',

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.

I am guessing ai wrote this script because it tends to use these unicode chars for success and failure. Lets remove those unicode chars.


if (!hasError) {
stdout.write(
stdout.supportsAnsiEscapes

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.

is this complexity needed? What value do we get from having ansiExcapes here.


// Run static analysis on staged files.
var analyzeHasError = false;
stdout.write('Running static analysis...');

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.

Structurally my gut is organizing code by job type instead of by when in the hooks process the code runs is more likely to be a durable standard.

@camsim99

Copy link
Copy Markdown
Contributor Author

@reidbaker To answer your high level question, I initially just called the plugin tool but it was extremely slow to the point where I wouldn't have felt comfortable landing this, so that's why I made optimizations around avoiding setup of non-required native tool chains and ignoring files that are not staged. However, your point is taken because the right way for me to fix things is to add the optimizations to the plugin tool itself. Working on that now! That should make the pre-commit hook itself much simpler, but I'll still refactor as needed.

I was unaware of https://pre-commit.com/#usage and modeled this PR after the engine. If you prefer we use the pre-commit tool, I can look into that, but either way I need to make some optimizations to the plugin tool to make this hook usable.

@reidbaker

Copy link
Copy Markdown
Contributor

Fwiw I don't have a strong preference but I wanted to point out the duplication especially since there was a tool that was aware in the repo. Adding optimizations to the tool seems like a good plan.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants