Read the PR diff and build logs and feed it to gemini in prompt (#5137)
https://github.com/flutter/flutter/issues/191099
diff --git a/app_dart/lib/src/request_handlers/analyze_logs.dart b/app_dart/lib/src/request_handlers/analyze_logs.dart
index c9e4b00..42d580c 100644
--- a/app_dart/lib/src/request_handlers/analyze_logs.dart
+++ b/app_dart/lib/src/request_handlers/analyze_logs.dart
@@ -3,12 +3,14 @@
// found in the LICENSE file.
import 'dart:async';
+import 'dart:convert';
import 'package:buildbucket/buildbucket_pb.dart' as bbv2;
import 'package:cocoon_server/logging.dart' show log;
import 'package:collection/collection.dart';
import 'package:fixnum/fixnum.dart';
import 'package:github/github.dart';
+import 'package:http/http.dart' as http;
import 'package:retry/retry.dart';
import '../../cocoon_service.dart';
@@ -92,8 +94,10 @@
);
}
- final (:stdoutLogs, :build) = await getBuildStepLogs(buildId: buildId);
- if (stdoutLogs.isEmpty) {
+ final (stdoutLogs: logsUrls, :build) = await getBuildStepLogs(
+ buildId: buildId,
+ );
+ if (logsUrls.isEmpty) {
throw NotFoundException('Logs Not Found for BuildId: $buildId');
}
String prDiffUrl;
@@ -108,13 +112,36 @@
}
log.info(
- '$logCrumb For PR diff url: $prDiffUrl Analyzing log urls: ${stdoutLogs.join(',')}',
+ '$logCrumb For PR diff url: $prDiffUrl Analyzing log urls: ${logsUrls.join(',')}',
);
+ String prDiff;
+ try {
+ prDiff = await http
+ .read(Uri.parse(prDiffUrl))
+ .timeout(const Duration(seconds: 30));
+ } catch (e, stackTrace) {
+ log.error('Failed to read PR diff from $prDiffUrl.', e, stackTrace);
+ rethrow;
+ }
+
+ final logs = <String>[];
+ for (final logUrl in logsUrls) {
+ final rawLogUrl = '$logUrl?format=raw';
+ try {
+ final logContent = await http
+ .read(Uri.parse(rawLogUrl))
+ .timeout(const Duration(seconds: 30));
+ logs.add(logContent);
+ } catch (e, stackTrace) {
+ log.error('Failed to read log from $rawLogUrl.', e, stackTrace);
+ rethrow;
+ }
+ }
// 4. Feed text to genkit.
final prompt =
- '''You are a Senior Infrastructure Engineer specializing in the Flutter CI ecosystem.
-I will provide you with a link to github pull request and the logs of a failed build step in a LUCI build associated with that change.
+ '''You are a Senior Infrastructure Engineer specializing in the Flutter and Flutter Packages CI ecosystem.
+In a Context section there are github pull request diff and the logs of a failed build step of a LUCI build associated with that change.
## Your task
@@ -172,10 +199,13 @@
- Linker error messages (e.g., `undefined reference to`).
- Summary messages in the check-runs API output like `1 build failed: [<build_name>]`.
-## Links
-
-Link to GitHub Pull Request Diff: $prDiffUrl
-Links to Logs: ${stdoutLogs.join('\n')}
+## Context
+<PULL_REQUEST_DIFF>
+${jsonEncode(prDiff)}
+</PULL_REQUEST_DIFF>
+<LOGS>
+${jsonEncode(logs)}
+</LOGS>
''';
final analysis = await _logAnalyzer.analyze(prompt: prompt);
@@ -220,7 +250,7 @@
if (step.logs.isNotEmpty) {
for (final log in step.logs) {
if (log.name == 'stdout') {
- stdoutLogs.add(log.hasViewUrl() ? log.viewUrl : log.url);
+ stdoutLogs.add(log.viewUrl);
}
}
} else if (kSubbuildPattern.hasMatch(step.summaryMarkdown)) {
diff --git a/app_dart/test/request_handlers/analyze_logs_test.dart b/app_dart/test/request_handlers/analyze_logs_test.dart
index f5a3856..6459c88 100644
--- a/app_dart/test/request_handlers/analyze_logs_test.dart
+++ b/app_dart/test/request_handlers/analyze_logs_test.dart
@@ -13,6 +13,8 @@
import 'package:cocoon_service/src/request_handling/exceptions.dart';
import 'package:cocoon_service/src/service/log_analyzer.dart';
import 'package:fixnum/fixnum.dart';
+import 'package:http/http.dart' as http;
+import 'package:http/testing.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
@@ -100,7 +102,8 @@
..logs.addAll([
bbv2.Log.create()
..name = 'stdout'
- ..url = 'http://logs/stdout',
+ ..url = 'http://logs/stdout'
+ ..viewUrl = 'http://logs/stdout',
]),
])
..tags.addAll([
@@ -123,7 +126,20 @@
'build_id': '9223372036854775807',
};
- final response = await tester.post(handler);
+ final mockHttpClient = MockClient((request) async {
+ if (request.url.toString() == 'http://github/pr/1.diff') {
+ return http.Response('fake diff', 200);
+ }
+ if (request.url.toString() == 'http://logs/stdout?format=raw') {
+ return http.Response('fake log content', 200);
+ }
+ return http.Response('not found', 404);
+ });
+
+ final response = await http.runWithClient(
+ () => tester.post(handler),
+ () => mockHttpClient,
+ );
expect(response.statusCode, HttpStatus.ok);
final updatedCheck = PresubmitJob.fromDocument(