Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ class ReadinessChecker {
if (!await _checkDependencies(workspaceRoot)) {
isReady = false;
}
if (!await _activateFlutterPluginTools(workspaceRoot)) {
isReady = false;
}
}

if (isReady) {
Expand Down Expand Up @@ -137,4 +140,48 @@ class ReadinessChecker {
_log('Dependencies are resolved and ready.');
return true;
}

Future<bool> _activateFlutterPluginTools(String workspaceRoot) async {
_log('5. Activating flutter_plugin_tools...');
final String? repoRoot = _findRepoRoot(workspaceRoot);
if (repoRoot == null) {
_log('Error: Failed to find repository root (no .git directory found).');
return false;
}
final ProcessResult activateResult = await _processManager.run(
[
'dart',
'pub',
'global',
'activate',
'--source',
'path',
_fileSystem.path.join(repoRoot, 'script', 'tool')
],
workingDirectory: workspaceRoot,
);
if (activateResult.exitCode != 0) {
_log('Error: Failed to globally activate flutter_plugin_tools.');
_log(activateResult.stderr);
return false;
}
_log('flutter_plugin_tools activated successfully.');
return true;
}

String? _findRepoRoot(String startPath) {
Directory dir = _fileSystem.directory(startPath);
while (true) {
final String gitPath = _fileSystem.path.join(dir.path, '.git');
if (_fileSystem.typeSync(gitPath) != FileSystemEntityType.notFound) {
return dir.path;
}
final Directory parent = dir.parent;
if (parent.path == dir.path) {
break;
}
dir = parent;
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ void main() {
log: (Object? message) => printLogs.add(message.toString()),
);
workspaceRoot = fileSystem.path.absolute('workspace');
fileSystem.file(fileSystem.path.join(workspaceRoot, '.git')).createSync(recursive: true);
printLogs.clear();
});

Expand Down Expand Up @@ -188,6 +189,9 @@ void main() {
log: (Object? message) => printLogs.add(message.toString()),
);
winWorkspaceRoot = r'C:\workspace';
winFileSystem
.file(winFileSystem.path.join(winWorkspaceRoot, '.git'))
.createSync(recursive: true);
printLogs.clear();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,25 @@ and then merge conflicts must be resolved.

Tests ensure that your changes do not break existing functionality
and that new features work as expected.
All unit tests must pass before code can be merged.
All unit tests (both Dart and native Android) must pass before code can be merged.

### Dart Unit Tests
Command to run:

```bash
cd $(git rev-parse --show-toplevel)
dart run script/tool/bin/flutter_plugin_tools.dart \
dart pub global run flutter_plugin_tools \

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 really like this change where you modify the check-readyness skills then can make the future skills easier to author and maintain.

dart-test --packages camera_android_camerax
```

If this command fails, the code is likely not ready to push.
### Native Unit Tests
Command to run:

```bash
dart pub global run flutter_plugin_tools \
native-test --android --packages camera_android_camerax --no-integration
```

If either command fails, the code is not ready to push.
The tests might have been failing prior to any changes being made,
so prompt the user to review all found errors
and fix the newly introduced failures before pushing any code.
Expand All @@ -88,8 +97,7 @@ and add a corresponding entry describing the change in `CHANGELOG.md`.
Command to run:

```bash
cd $(git rev-parse --show-toplevel)
dart run script/tool/bin/flutter_plugin_tools.dart \
dart pub global run flutter_plugin_tools \
publish-check --packages camera_android_camerax
```

Expand All @@ -104,8 +112,7 @@ the standard copyright and license header.
Command to run:

```bash
cd $(git rev-parse --show-toplevel)
dart run script/tool/bin/flutter_plugin_tools.dart license-check --packages camera_android_camerax
dart pub global run flutter_plugin_tools license-check --packages camera_android_camerax
```

If this command fails, the code WAS NOT ready to push.
Expand All @@ -123,6 +130,10 @@ Virtually all changes require a test.
See [Test Documentation](https://github.com/flutter/flutter/blob/master/docs/ecosystem/testing/Plugin-Tests.md).
Evaluate the change against that testing rubric.

Specifically check:
- **Dart changes**: If Dart source files in `lib/` (excluding generated files, e.g., `.g.dart` files) were modified or added, verify that corresponding Dart tests in `test/` were added or updated.
- **Native Android changes**: If native Android source files (`.java`, `.kt` in `android/src/main/`, excluding Pigeon generated files like `.g.java` and `.g.kt`) were modified or added, verify that corresponding native unit tests in `android/src/test/` were added or updated.

Based on the rubric, if the change requires a test,
give the user a quote from the testing documentation
on what type of test is required for their changes.
Expand Down

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.

Meta comment these are expensive tests to run and I predict we will want to minimize the number of these and maximize the things being evaluated per eval. Similar to an integration test.

No changed needed for now but next time we are adding evals for a particular pre-push situation lets try to pick one of the three test_data/setup_* scripts to update.

Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"repo_criteria": [],
"evals": [
{
"id": 1,
"prompt": "First, run 'dart run .agents/skills/pre-push-skill/evals/test_data/setup_missing_native_test.dart'. Then, run the /pre-push skill. Do not attempt to fix any issues you find, only report the output of the skill.",
"expected_chat_output": [
"Agent explicitly states that the code is not ready to push.",
"Agent points out that DummyEvalFeature.java was modified.",
"Agent points out that a corresponding native unit test was not added or updated."
],
"expected_repo_state": [
"No changes are pushed to the remote repository.",
"The agent does not create any new commits or modify the working tree."
],
"agent_config": "reidbaker-agent"

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.

Non blocking: I think we should probably have contributor-agent, oneshot-agent and bare-agent as the groups of skills.

where maybe we have contributor-agent be the default not actually an agent you select and where onshot-agent has tighter expectations.

},
{
"id": 2,
"prompt": "First, run 'dart run .agents/skills/pre-push-skill/evals/test_data/setup_success_native_test.dart'. Then, run the /pre-push skill. Do not attempt to fix any issues you find, only report the output of the skill.",
"expected_chat_output": [
Comment thread
camsim99 marked this conversation as resolved.
"Agent explicitly states whether the code is ready to push.",

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.

shouldn't this say "Agent explicitly states the code is ready to push."

Suggested change
"Agent explicitly states whether the code is ready to push.",
"Agent explicitly states the code is ready to push.",

"Agent confirms that native unit tests pass."
],
"expected_repo_state": [
"No changes are pushed to the remote repository.",
"The agent does not create any new commits or modify the working tree."
],
"agent_config": "reidbaker-agent"
},
{
"id": 3,
"prompt": "First, run 'dart run .agents/skills/pre-push-skill/evals/test_data/setup_mixed_missing_native_test.dart'. Then, run the /pre-push skill. Do not attempt to fix any issues you find, only report the output of the skill.",
"expected_chat_output": [
"Agent explicitly states that the code is not ready to push.",
"Agent points out that DummyEvalFeature.java lacks a corresponding native unit test update."
],
"expected_repo_state": [
"No changes are pushed to the remote repository.",
"The agent does not create any new commits or modify the working tree."
],
"agent_config": "reidbaker-agent"
},
{
"id": 4,
"prompt": "First, run 'dart run .agents/skills/pre-push-skill/evals/test_data/setup_missing_native_test.dart'. The Java change is just a comment and I explicitly approve bypassing the native test requirement. Then, run the /pre-push skill. Do not attempt to fix any issues you find, only report the output of the skill.",
"expected_chat_output": [
"Agent acknowledges the user's explicit approval to bypass the native test requirement.",
"Agent states that the code is ready to push.",
"Agent lists the steps that passed or were explicitly bypassed."
],
"expected_repo_state": [
"No changes are pushed to the remote repository.",
"The agent does not create any new commits or modify the working tree."
],
"agent_config": "reidbaker-agent"
}
]
}

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.

Non blocking comment but does cognitive complexity run on the files in test_data? If not consider adding it in a follow up pr. or at least filing an issue.

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:io';

void main() {
final ProcessResult branchResult = Process.runSync('git', ['branch', '--show-current']);
final String branch = branchResult.stdout.toString().trim();
if (branch == 'main') {
stdout.writeln('Error: Cannot run setup scripts on main branch.');
exit(1);
}

final javaFile = File('android/src/main/java/io/flutter/plugins/camerax/DummyEvalFeature.java');
javaFile.createSync(recursive: true);
javaFile.writeAsStringSync('''
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camerax;

public class DummyEvalFeature {
public void doNothing() {}
}
''');

Process.runSync('git', ['add', javaFile.path]);
Process.runSync('git', [
'-c',
'user.name=Author',
'-c',
'user.email=author@example.com',
'commit',
'-m',
'Add DummyEvalFeature.java without tests',
]);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:io';

void main() {
final ProcessResult branchResult = Process.runSync('git', ['branch', '--show-current']);
final String branch = branchResult.stdout.toString().trim();
if (branch == 'main') {
stdout.writeln('Error: Cannot run setup scripts on main branch.');
exit(1);
}

final javaFile = File('android/src/main/java/io/flutter/plugins/camerax/DummyEvalFeature.java');
javaFile.createSync(recursive: true);
javaFile.writeAsStringSync('''
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camerax;

public class DummyEvalFeature {
public void doNothing() {}
}
''');

final dartFile = File('lib/src/dummy_eval_feature.dart');
dartFile.createSync(recursive: true);
dartFile.writeAsStringSync('''
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/// A dummy eval feature
class DummyEvalFeature {
/// Do nothing
void doNothing() {}
}
''');

final dartTestFile = File('test/dummy_eval_feature_test.dart');
dartTestFile.createSync(recursive: true);
dartTestFile.writeAsStringSync('''
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:camera_android_camerax/src/dummy_eval_feature.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
test('dummy', () {
final feature = DummyEvalFeature();
feature.doNothing();
});
}
''');

Process.runSync('git', ['add', javaFile.path, dartFile.path, dartTestFile.path]);
Process.runSync('git', [
'-c',
'user.name=Author',
'-c',
'user.email=author@example.com',
'commit',
'-m',
'Add DummyEvalFeature with Dart test but missing Java test',
]);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:io';

void main() {
final ProcessResult branchResult = Process.runSync('git', ['branch', '--show-current']);
final String branch = branchResult.stdout.toString().trim();
if (branch == 'main') {
stdout.writeln('Error: Cannot run setup scripts on main branch.');
exit(1);
}

final javaFile = File('android/src/main/java/io/flutter/plugins/camerax/DummyEvalFeature.java');
javaFile.createSync(recursive: true);
javaFile.writeAsStringSync('''
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camerax;

public class DummyEvalFeature {
public void doNothing() {}
}
''');

final javaTestFile = File(
'android/src/test/java/io/flutter/plugins/camerax/DummyEvalFeatureTest.java',
);
javaTestFile.createSync(recursive: true);
javaTestFile.writeAsStringSync('''
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camerax;

import org.junit.Test;
import static org.junit.Assert.assertTrue;

public class DummyEvalFeatureTest {
@Test
public void testDoNothing() {
DummyEvalFeature feature = new DummyEvalFeature();
feature.doNothing();
assertTrue(true);
}
}
''');

Process.runSync('git', ['add', javaFile.path, javaTestFile.path]);
Process.runSync('git', [
'-c',
'user.name=Author',
'-c',
'user.email=author@example.com',
'commit',
'-m',
'Add DummyEvalFeature.java and tests',
]);
}
6 changes: 5 additions & 1 deletion packages/camera/camera_android_camerax/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
- Mocks (`dart run build_runner build -d`): Run after modifying mocked
classes or adding new mocks. (see [dart-generate-test-mocks](.agents/skills/dart-generate-test-mocks/SKILL.md))
- **Verify Tests**: All tests must pass before landing. Add or update tests for
any new logic. For integration tests, see [flutter-add-integration-test](.agents/skills/flutter-add-integration-test/SKILL.md).
any new logic.
- **Dart Unit Tests**: See [dart-add-unit-test](.agents/skills/dart-add-unit-test/SKILL.md).
- **Native Unit Tests**: Run `dart pub global run flutter_plugin_tools native-test --android --packages camera_android_camerax --no-integration`.
- **Integration Tests**: See [flutter-add-integration-test](.agents/skills/flutter-add-integration-test/SKILL.md).
- **Run Pre-Push Checks**: Run [pre-push-skill](.agents/skills/pre-push-skill/SKILL.md)
before pushing to prevent CI failures and code review blocks.

Expand All @@ -24,5 +27,6 @@
and [dart-collect-coverage](.agents/skills/dart-collect-coverage/SKILL.md).
- Avoid duplicating constant strings; reuse existing ones from adjacent code.
- **Testing Guidelines**: You MUST read and follow all rules in [TESTING.md](TESTING.md) BEFORE writing or modifying any tests. This is CRITICAL for preventing CI flakiness.
- **Native Unit Tests**: When modifying `.java` or `.kt` files in `android/src/main/` (excluding Pigeon generated files like `.g.java` or `.g.kt`) with logic changes, you MUST add or update corresponding native unit test files in `android/src/test/`.
- **CRITICAL**: When spawning subagents, NEVER provide absolute file paths in prompts. ALWAYS use relative paths. Passing absolute paths breaks `Workspace: branch` isolation and causes state bleed into the active workspace.
- **Validation**: Never run `.ci/scripts/*` or `script/tool_runner.sh` globally to validate local changes. They are slow and modify the entire repository. Always use targeted skills (like `dart-run-static-analysis` or `pre-push-skill`) to run the repo tool scoped to this package.
Loading